diff --git a/core/lib/Drupal/Component/Utility/Number.php b/core/lib/Drupal/Component/Utility/Number.php index 7cc19b9214..f8d7f0fbd9 100644 --- a/core/lib/Drupal/Component/Utility/Number.php +++ b/core/lib/Drupal/Component/Utility/Number.php @@ -37,6 +37,9 @@ class Number { public static function normalize($number) { // Convert non-strings to strings, for consistent and lossless processing. if (is_float($number)) { + if (preg_match(sprintf('/^\d+\.\d{1,%d}$/', static::IEEE_754_DOUBLE_GUARANTEED_SIGNIFICANT_DECIMALS), (string) $number)) { + return (string) $number; + } return rtrim(number_format($number, self::IEEE_754_DOUBLE_GUARANTEED_SIGNIFICANT_DECIMALS, '.', ''), '0'); } elseif (is_int($number)) { @@ -98,15 +101,20 @@ public static function countSignificantDecimals($number) { * * @see http://opensource.apple.com/source/WebCore/WebCore-1298/html/NumberInputType.cpp */ - public static function validStep($value, $step, $offset = 0.0) { - $float_value = (float) abs($value - $offset); + public static function validStep($value, $step, $offset = null) { + // Confirm the step is positive. + if ($step <= 0) { + return FALSE; + } + + $float_value = (float) abs(is_null($offset) ? $value : $value - $offset); // The expected number significant decimals is dictated by the step. $expected_significant_decimals = static::countSignificantDecimals($step) + 1; // If the actual value has more significant decimals than expected, it has a // higher precision than desired it isn't divisible by the step. - $actual_significant_decimals = static::countSignificantDecimals((string) $float_value); + $actual_significant_decimals = static::countSignificantDecimals($float_value); if ($actual_significant_decimals > $expected_significant_decimals) { return FALSE; } diff --git a/core/tests/Drupal/Tests/Component/Utility/NumberTest.php b/core/tests/Drupal/Tests/Component/Utility/NumberTest.php index f951a339a1..7cd2f050bc 100644 --- a/core/tests/Drupal/Tests/Component/Utility/NumberTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/NumberTest.php @@ -67,7 +67,7 @@ public function provideGetPrecision() { [1, '0.0'], [0, -0.0], [1, '-0.0'], - # The maximum supported float precision is 15 decimals. + // The maximum supported float precision is 15 decimals. [0, 0.0000000000000], [0, -0.0000000000000], [9, -0.0000000090000], @@ -77,10 +77,10 @@ public function provideGetPrecision() { [15, -0.0000000090000009], [15, -0.00000000900000099], [15, -0.000000009000000900009], - # Numeric strings do not suffer from the system-specific limitations to - # float precision, so they can contain many more significant decimals. - # This is especially useful when working with solutions such as BCMath - # (https://secure.php.net/manual/en/book.bc.php) + // Numeric strings do not suffer from the system-specific limitations to + // float precision, so they can contain many more significant decimals. + // This is especially useful when working with solutions such as BCMath + // (https://secure.php.net/manual/en/book.bc.php) [15, '0.000000000000000'], [15, '-0.000000000000000'], [15, '-0.000000009000000'], @@ -127,7 +127,6 @@ public static function providerTestValidStep() { [42, 10.5, TRUE], [1, 1 / 3, TRUE], [-100, 100 / 7, TRUE], - [1000, -10, TRUE], // Valid and very small float steps. [1936.5, 3e-8, TRUE], @@ -137,10 +136,12 @@ public static function providerTestValidStep() { // Invalid integer steps. [100, 30, FALSE], [-10, 4, FALSE], + [1000, -10, FALSE], // Invalid float steps. [6, 5 / 7, FALSE], [10.3, 10.25, FALSE], + [1000, -10.0, FALSE], // Step mismatches very close to being valid. [70 + 9e-7, 10 + 9e-7, FALSE], @@ -166,6 +167,10 @@ public static function providerTestValidStep() { // Precision of the value is higher than that of the step. [990088999.0099, 0.001, FALSE], [990088999.0099, 0.01, FALSE], + + // Ensure floats with more significant decimals that the guaranteed + // minimum are normalized. + [0.00000000000000900009, 0.00000000000000100001, TRUE], ]; }