Number.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. <?php
  2. namespace Drupal\Component\Utility;
  3. /**
  4. * Provides helper methods for manipulating numbers.
  5. *
  6. * @ingroup utility
  7. */
  8. class Number {
  9. /**
  10. * Verifies that a number is a multiple of a given step.
  11. *
  12. * The implementation assumes it is dealing with IEEE 754 double precision
  13. * floating point numbers that are used by PHP on most systems.
  14. *
  15. * This is based on the number/range verification methods of webkit.
  16. *
  17. * @param float $value
  18. * The value that needs to be checked.
  19. * @param float $step
  20. * The step scale factor. Must be positive.
  21. * @param float $offset
  22. * (optional) An offset, to which the difference must be a multiple of the
  23. * given step.
  24. *
  25. * @return bool
  26. * TRUE if no step mismatch has occurred, or FALSE otherwise.
  27. *
  28. * @see http://opensource.apple.com/source/WebCore/WebCore-1298/html/NumberInputType.cpp
  29. */
  30. public static function validStep($value, $step, $offset = 0.0) {
  31. $double_value = (double) abs($value - $offset);
  32. // The fractional part of a double has 53 bits. The greatest number that
  33. // could be represented with that is 2^53. If the given value is even bigger
  34. // than $step * 2^53, then dividing by $step will result in a very small
  35. // remainder. Since that remainder can't even be represented with a single
  36. // precision float the following computation of the remainder makes no sense
  37. // and we can safely ignore it instead.
  38. if ($double_value / pow(2.0, 53) > $step) {
  39. return TRUE;
  40. }
  41. // Now compute that remainder of a division by $step.
  42. $remainder = (double) abs($double_value - $step * round($double_value / $step));
  43. // $remainder is a double precision floating point number. Remainders that
  44. // can't be represented with single precision floats are acceptable. The
  45. // fractional part of a float has 24 bits. That means remainders smaller than
  46. // $step * 2^-24 are acceptable.
  47. $computed_acceptable_error = (double) ($step / pow(2.0, 24));
  48. return $computed_acceptable_error >= $remainder || $remainder >= ($step - $computed_acceptable_error);
  49. }
  50. /**
  51. * Generates a sorting code from an integer.
  52. *
  53. * Consists of a leading character indicating length, followed by N digits
  54. * with a numerical value in base 36 (alphadecimal). These codes can be sorted
  55. * as strings without altering numerical order.
  56. *
  57. * It goes:
  58. * 00, 01, 02, ..., 0y, 0z,
  59. * 110, 111, ... , 1zy, 1zz,
  60. * 2100, 2101, ..., 2zzy, 2zzz,
  61. * 31000, 31001, ...
  62. *
  63. * @param int $i
  64. * The integer value to convert.
  65. *
  66. * @return string
  67. * The alpha decimal value.
  68. *
  69. * @see \Drupal\Component\Utility\Number::alphadecimalToInt
  70. */
  71. public static function intToAlphadecimal($i = 0) {
  72. $num = base_convert((int) $i, 10, 36);
  73. $length = strlen($num);
  74. return chr($length + ord('0') - 1) . $num;
  75. }
  76. /**
  77. * Decodes a sorting code back to an integer.
  78. *
  79. * @param string $string
  80. * The alpha decimal value to convert
  81. *
  82. * @return int
  83. * The integer value.
  84. *
  85. * @see \Drupal\Component\Utility\Number::intToAlphadecimal
  86. */
  87. public static function alphadecimalToInt($string = '00') {
  88. return (int) base_convert(substr($string, 1), 36, 10);
  89. }
  90. }