Bitwise.php 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. <?php
  2. namespace MathPHP\Functions;
  3. class Bitwise
  4. {
  5. /**
  6. * Add two ints ignoring the signing bit.
  7. *
  8. * 8 bit examples:
  9. * 0d15 + 0d1 = 0d16
  10. * 0b00001111 + 0b00000001 = 0b00010000
  11. *
  12. * 0d127 + 0d1 = 0d-128
  13. * 0b01111111 + 0b00000001 = 0b10000000
  14. *
  15. * 0d-1 + 0d1 = 0d0
  16. * 0b11111111 + 0b00000001 = 0b00000000 :overflow = true
  17. *
  18. * 0d-1 + 0d-1 = 0d-2
  19. * 0b11111111 + 0b11111111 = 0b11111110: overflow = true
  20. *
  21. * Scenarios
  22. * 1) Result is an integer
  23. * $a and $b are negative, the most significant bit will overflow.
  24. * If only one is negative, the most significant bit will overflow if the sum is positive.
  25. * 2) Result is not an integer
  26. * a) a and b are positive
  27. * If $a + $b overflows as a signed int, it is now a negative int, but the most significant
  28. * bit will not overflow.
  29. * b) a and b are not both positive
  30. * The sum of two "large" negative numbers will both overflow the most significant bit
  31. * and the signed int.
  32. * The values of $a and $b have to be shifted towards zero to prevent the
  33. * signed int from overflowing. We are removing the most significant
  34. * bit from the ints by subtracting PHP_INT_MIN to prevent overflow.
  35. * $a = 1001, $b = 1010, return [true, '0011] because PHP_INT_MIN = 1000,
  36. * Giving $a - 1000 = 0001, $b - 1000 = 0010.
  37. *
  38. * @param int $a
  39. * @param int $b
  40. *
  41. * @return array{overflow: bool, value: int|float}
  42. * 'overflow' is true if the result is larger than the bits in an int
  43. * 'value' is the result of the addition ignoring any overflow.
  44. */
  45. public static function add(int $a, int $b): array
  46. {
  47. /** @var int|float due to potential overflow */
  48. $sum = $a + $b;
  49. if (\is_int($sum)) {
  50. $overflow = (($a < 0 || $b < 0) && $sum >= 0) || ($a < 0 && $b < 0);
  51. } elseif ($a > 0 && $b > 0) {
  52. $sum = $a - \PHP_INT_MAX + $b - 1 + \PHP_INT_MIN;
  53. $overflow = false;
  54. } else {
  55. $a -= \PHP_INT_MIN;
  56. $b -= \PHP_INT_MIN;
  57. $sum = $a + $b;
  58. $overflow = true;
  59. }
  60. return [
  61. 'overflow' => $overflow,
  62. 'value' => $sum,
  63. ];
  64. }
  65. }