diff --git a/ext/standard/math.c b/ext/standard/math.c index 6b16b0755f69b..30be591e18a42 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -30,50 +30,6 @@ #include "basic_functions.h" -/* {{{ php_intlog10abs - Returns floor(log10(fabs(val))), uses fast binary search */ -static inline int php_intlog10abs(double value) { - value = fabs(value); - - if (value < 1e-8 || value > 1e22) { - return (int)floor(log10(value)); - } else { - /* Do a binary search with 5 steps */ - int result = 15; - static const double values[] = { - 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, - 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, - 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; - - if (value < values[result]) { - result -= 8; - } else { - result += 8; - } - if (value < values[result]) { - result -= 4; - } else { - result += 4; - } - if (value < values[result]) { - result -= 2; - } else { - result += 2; - } - if (value < values[result]) { - result -= 1; - } else { - result += 1; - } - if (value < values[result]) { - result -= 1; - } - result -= 8; - return result; - } -} -/* }}} */ - /* {{{ php_intpow10 Returns pow(10.0, (double)power), uses fast lookup table for exact powers */ static inline double php_intpow10(int power) { @@ -165,56 +121,30 @@ static inline double php_round_helper(double value, int mode) { /* {{{ _php_math_round */ /* - * Rounds a number to a certain number of decimal places in a certain rounding - * mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding + * Rounds a number to a certain number of decimal places in a certain rounding mode. + * If you "HALF UP" a value like 0.285 (0.28499999999999998), it will be rounded to 0.28. */ PHPAPI double _php_math_round(double value, int places, int mode) { - double f1, f2; + double f1; double tmp_value; - int precision_places; if (!zend_finite(value) || value == 0.0) { return value; } places = places < INT_MIN+1 ? INT_MIN+1 : places; - precision_places = 14 - php_intlog10abs(value); f1 = php_intpow10(abs(places)); - /* If the decimal precision guaranteed by FP arithmetic is higher than - the requested places BUT is small enough to make sure a non-zero value - is returned, pre-round the result to the precision */ - if (precision_places > places && precision_places - 15 < places) { - int64_t use_precision = precision_places < INT_MIN+1 ? INT_MIN+1 : precision_places; - - f2 = php_intpow10(abs((int)use_precision)); - if (use_precision >= 0) { - tmp_value = value * f2; - } else { - tmp_value = value / f2; - } - /* preround the result (tmp_value will always be something * 1e14, - thus never larger than 1e15 here) */ - tmp_value = php_round_helper(tmp_value, mode); - - use_precision = places - precision_places; - use_precision = use_precision < INT_MIN+1 ? INT_MIN+1 : use_precision; - /* now correctly move the decimal point */ - f2 = php_intpow10(abs((int)use_precision)); - /* because places < precision_places */ - tmp_value = tmp_value / f2; + /* adjust the value */ + if (places >= 0) { + tmp_value = value * f1; } else { - /* adjust the value */ - if (places >= 0) { - tmp_value = value * f1; - } else { - tmp_value = value / f1; - } - /* This value is beyond our precision, so rounding it is pointless */ - if (fabs(tmp_value) >= 1e15) { - return value; - } + tmp_value = value / f1; + } + /* This value is beyond our precision, so rounding it is pointless */ + if (fabs(tmp_value) >= 1e15) { + return value; } /* round the temp value */ diff --git a/ext/standard/tests/math/bug24142.phpt b/ext/standard/tests/math/bug24142.phpt index 947deaebd6692..739ac25421dc3 100644 --- a/ext/standard/tests/math/bug24142.phpt +++ b/ext/standard/tests/math/bug24142.phpt @@ -2,19 +2,65 @@ Bug #24142 (round() problems) --FILE-- ".round($v, 2)."\n"; - $v += 0.01; -} +echo "round(0.005, 2)\n"; +var_dump(round(0.005, 2)); +echo "\n"; + +echo "round(0.015, 2)\n"; +var_dump(round(0.015, 2)); +echo "\n"; + +echo "round(0.025, 2)\n"; +var_dump(round(0.025, 2)); +echo "\n"; + +echo "round(0.035, 2)\n"; +var_dump(round(0.035, 2)); +echo "\n"; + +echo "round(0.045, 2)\n"; +var_dump(round(0.045, 2)); +echo "\n"; + +echo "round(0.055, 2)\n"; +var_dump(round(0.055, 2)); +echo "\n"; + +echo "round(0.065, 2)\n"; +var_dump(round(0.065, 2)); +echo "\n"; + +echo "round(0.075, 2)\n"; +var_dump(round(0.075, 2)); +echo "\n"; + +echo "round(0.085, 2)\n"; +var_dump(round(0.085, 2)); ?> --EXPECT-- -round(0.005, 2) -> 0.01 -round(0.015, 2) -> 0.02 -round(0.025, 2) -> 0.03 -round(0.035, 2) -> 0.04 -round(0.045, 2) -> 0.05 -round(0.055, 2) -> 0.06 -round(0.065, 2) -> 0.07 -round(0.075, 2) -> 0.08 -round(0.085, 2) -> 0.09 +round(0.005, 2) +float(0.01) + +round(0.015, 2) +float(0.02) + +round(0.025, 2) +float(0.03) + +round(0.035, 2) +float(0.04) + +round(0.045, 2) +float(0.05) + +round(0.055, 2) +float(0.06) + +round(0.065, 2) +float(0.07) + +round(0.075, 2) +float(0.08) + +round(0.085, 2) +float(0.09) diff --git a/ext/standard/tests/math/round_gh12143_remove_pre_rounding.phpt b/ext/standard/tests/math/round_gh12143_remove_pre_rounding.phpt new file mode 100644 index 0000000000000..fa07a36e6bd3a --- /dev/null +++ b/ext/standard/tests/math/round_gh12143_remove_pre_rounding.phpt @@ -0,0 +1,83 @@ +--TEST-- +Fix GH-12143: Remove pre-rounding +--FILE-- + +--EXPECT-- +HALF_UP +float(1.7000000000001) +float(-1.7000000000001) +float(123456789012340) +float(-123456789012340) +float(0.28) +float(-0.28) + +HALF_DOWN +float(1.7000000000001) +float(-1.7000000000001) +float(123456789012340) +float(-123456789012340) +float(2) +float(-2) +float(1.24) +float(-1.24) + +HALF_EVEN +float(1.7000000000002) +float(-1.7000000000002) +float(1.7000000000008) +float(-1.7000000000008) +float(12345678901234) +float(-12345678901234) +float(2) +float(-2) + +HALF_ODD +float(1.7000000000003) +float(-1.7000000000003) +float(1.7000000000007) +float(-1.7000000000007) +float(12345678901233) +float(-12345678901233) +float(2) +float(-2) diff --git a/ext/standard/tests/math/round_prerounding.phpt b/ext/standard/tests/math/round_prerounding.phpt deleted file mode 100644 index 0419d6c735a46..0000000000000 --- a/ext/standard/tests/math/round_prerounding.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -round() prerounds results to precision ---INI-- -precision=14 ---FILE-- - ---EXPECT-- -float(0.29)