MathUtils.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #pragma once
  2. #include "il2cpp-config.h"
  3. #include <cmath>
  4. #include <limits>
  5. #include <stdint.h>
  6. namespace il2cpp
  7. {
  8. namespace utils
  9. {
  10. namespace MathUtils
  11. {
  12. // Do math on low/high part separately as 64-bit integers because otherwise
  13. // we might easily overflow during initial multiplication
  14. inline int64_t A_Times_B_DividedBy_C(int64_t multiplicand, int64_t multiplier, int64_t divisor)
  15. {
  16. IL2CPP_ASSERT((llabs(divisor) & (1LL << 62)) == 0 && "Can't divide by numbers with absolute value larger than 2^62 - 1.");
  17. bool resultIsNegative = static_cast<uint64_t>(multiplicand ^ multiplier ^ divisor) >> 63; // Result is negative if odd number of operands are negative
  18. multiplicand = llabs(multiplicand);
  19. IL2CPP_ASSERT(multiplicand > 0 && "Can't multiply by -2^63.");
  20. multiplier = llabs(multiplier);
  21. IL2CPP_ASSERT(multiplier > 0 && "Can't multiply by -2^63.");
  22. divisor = llabs(divisor); // We already asserted on divisor size
  23. uint64_t multiplicand_low = multiplicand & 0xFFFFFFFF;
  24. uint64_t multiplicand_high = multiplicand >> 32;
  25. uint64_t multiplier_low = multiplier & 0xFFFFFFFF;
  26. uint64_t multiplier_high = multiplier >> 32;
  27. // We're gonna assume our multiplicated value is 128-bit integer
  28. // so we're gonna compose it of two uint64_t's
  29. // a * b =
  30. // (a_high * 2^32 + a_low) * (b_high * 2^32 + b_low) =
  31. // a_high * b_high * 2^64 + (a_high * b_low + a_low * b_high) * 2^32 + a_low * b_low
  32. uint64_t dividends[2] =
  33. {
  34. multiplicand_low * multiplier_low, // low part, bits [0, 63]
  35. multiplicand_high * multiplier_high // high part, bits [64, 127]
  36. };
  37. uint64_t resultMid1 = multiplicand_high * multiplier_low + multiplicand_low * multiplier_high; // mid part, bits [32, 95]
  38. dividends[1] += resultMid1 >> 32; // add the higher bits of mid part ([64, 95]) to high part
  39. resultMid1 = (resultMid1 & 0xFFFFFFFF) << 32; // Now this contains the lower bits of mid part ([32, 63])
  40. // Check for lower part overflow below adding the lower bits of mid part to it
  41. // Add carry to high part if overflow occurs
  42. if (dividends[0] > std::numeric_limits<uint64_t>::max() - resultMid1)
  43. dividends[1]++;
  44. dividends[0] += resultMid1; // add the lower bits of mid part to low part
  45. // At this point, we got our whole divident 128-bit value inside 'dividends'
  46. uint64_t workValue = 0; // Value that we're gonna be dividing
  47. uint64_t result = 0; // The final result
  48. const uint64_t kOne = 1;
  49. int bitIndex = 127; // Current bit that we're gonna be add to the workValue
  50. // Let's find the starting point for our division
  51. // We'll keep adding bits from our divident to the workValue until it's higher than the divisor
  52. // We did divisor = llabs(divisor) earlier, so cast to unsigned is safe
  53. while (workValue < static_cast<uint64_t>(divisor))
  54. {
  55. workValue <<= 1;
  56. if (bitIndex > -1)
  57. {
  58. workValue |= (dividends[bitIndex / 64] & (kOne << (bitIndex % 64))) != 0;
  59. }
  60. else
  61. {
  62. return 0;
  63. }
  64. bitIndex--;
  65. }
  66. // Main division loop
  67. for (; bitIndex > -2 || workValue >= static_cast<uint64_t>(divisor); bitIndex--)
  68. {
  69. result <<= 1; // Shift result left
  70. // Since it's binary, the division result can be only 0 and 1
  71. // It's 1 if workValue is higher or equal to divisor
  72. if (workValue >= static_cast<uint64_t>(divisor))
  73. {
  74. workValue -= static_cast<uint64_t>(divisor);
  75. result++;
  76. }
  77. // Shift work value to the left and append the next bit of our dividend
  78. IL2CPP_ASSERT((workValue & (1LL << 63)) == 0 && "overflow!");
  79. if (bitIndex > -1)
  80. {
  81. workValue <<= 1;
  82. workValue |= (dividends[bitIndex / 64] & (kOne << (bitIndex % 64))) != 0;
  83. }
  84. }
  85. // Negate result if it's supposed to be negative
  86. if (resultIsNegative)
  87. return -static_cast<int64_t>(result);
  88. return result;
  89. }
  90. }
  91. }
  92. }