diff --git a/NEWS b/NEWS index 7f59f831c9126..19d95ae07cf85 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.3 +- BcMath: + . Fixed bug GH-17049 (Correctly compare 0 and -0). (Saki Takamachi) + - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) @@ -12,7 +15,7 @@ PHP NEWS 05 Dec 2024, PHP 8.4.2 - BcMath: - . Fixed bug GH-16978 (Avoid unnecessary padding with leading zeros) + . Fixed bug GH-16978 (Avoid unnecessary padding with leading zeros). (Saki Takamachi) - COM: diff --git a/ext/bcmath/libbcmath/src/compare.c b/ext/bcmath/libbcmath/src/compare.c index f2df5cc24c57e..2c24dab777059 100644 --- a/ext/bcmath/libbcmath/src/compare.c +++ b/ext/bcmath/libbcmath/src/compare.c @@ -45,6 +45,18 @@ bcmath_compare_result _bc_do_compare(bc_num n1, bc_num n2, size_t scale, bool us /* First, compare signs. */ if (use_sign && n1->n_sign != n2->n_sign) { + /* + * scale and n->n_scale differ only in Number objects. + * This happens when explicitly specify the scale in a Number method. + */ + if ((n1->n_scale > scale || n2->n_scale > scale) && + n1->n_len == 1 && n2->n_len == 1 && + n1->n_value[0] == 0 && n2->n_value[0] == 0 && + bc_is_zero_for_scale(n1, scale) && bc_is_zero_for_scale(n2, scale) + ) { + /* e.g. 0.00 <=> -0.00 */ + return BCMATH_EQUAL; + } if (n1->n_sign == PLUS) { /* Positive N1 > Negative N2 */ return BCMATH_LEFT_GREATER; diff --git a/ext/bcmath/libbcmath/src/str2num.c b/ext/bcmath/libbcmath/src/str2num.c index a56fefa02a4c7..79c1d4216fe7b 100644 --- a/ext/bcmath/libbcmath/src/str2num.c +++ b/ext/bcmath/libbcmath/src/str2num.c @@ -173,6 +173,15 @@ bool bc_str2num(bc_num *num, const char *str, const char *end, size_t scale, siz if (str_scale > scale && !auto_scale) { fractional_end -= str_scale - scale; str_scale = scale; + + /* + * e.g. 123.0001 with scale 2 -> 123.00 + * So, remove the trailing 0 again. + */ + if (str_scale > 0) { + const char *fractional_new_end = bc_skip_zero_reverse(fractional_end, fractional_ptr); + str_scale -= fractional_new_end - fractional_end; + } } } else { if (full_scale) { diff --git a/ext/bcmath/tests/bccomp_variation002.phpt b/ext/bcmath/tests/bccomp_variation002.phpt index 299f454780601..145181083570b 100644 --- a/ext/bcmath/tests/bccomp_variation002.phpt +++ b/ext/bcmath/tests/bccomp_variation002.phpt @@ -13,6 +13,7 @@ echo bccomp("-2.29", "2.3", "2") . "\n"; echo bccomp("2.29", "-2.3", "2") . "\n"; echo bccomp("-2.29", "-2.3", "1") . "\n"; echo bccomp("-2.29", "0", "1") . "\n"; +echo bccomp("0.001", "-0.001", "1") . "\n"; ?> --EXPECT-- 0 @@ -22,3 +23,4 @@ echo bccomp("-2.29", "0", "1") . "\n"; 1 1 -1 +0 diff --git a/ext/bcmath/tests/number/methods/compare_near_zero.phpt b/ext/bcmath/tests/number/methods/compare_near_zero.phpt new file mode 100644 index 0000000000000..342b38294790b --- /dev/null +++ b/ext/bcmath/tests/number/methods/compare_near_zero.phpt @@ -0,0 +1,71 @@ +--TEST-- +BcMath\Number compare() with scale +--EXTENSIONS-- +bcmath +--FILE-- + {$value2}: ", 20, ' ', STR_PAD_LEFT); + $output .= str_pad((string) $value1->compare($value2, $scale), 2, ' ', STR_PAD_LEFT); + echo "{$output}\n"; + } + } +} +?> +--EXPECT-- +========== scale is 0 ========== + 0.001 <=> 0: 0 + 0.001 <=> 0: 0 + 0.001 <=> 0.0011: 0 + 0.001 <=> -0.0011: 0 + -0.001 <=> 0: 0 + -0.001 <=> 0: 0 + -0.001 <=> 0.0011: 0 +-0.001 <=> -0.0011: 0 +========== scale is 2 ========== + 0.001 <=> 0: 0 + 0.001 <=> 0: 0 + 0.001 <=> 0.0011: 0 + 0.001 <=> -0.0011: 0 + -0.001 <=> 0: 0 + -0.001 <=> 0: 0 + -0.001 <=> 0.0011: 0 +-0.001 <=> -0.0011: 0 +========== scale is 3 ========== + 0.001 <=> 0: 1 + 0.001 <=> 0: 1 + 0.001 <=> 0.0011: 0 + 0.001 <=> -0.0011: 1 + -0.001 <=> 0: -1 + -0.001 <=> 0: -1 + -0.001 <=> 0.0011: -1 +-0.001 <=> -0.0011: 0 +========== scale is 4 ========== + 0.001 <=> 0: 1 + 0.001 <=> 0: 1 + 0.001 <=> 0.0011: -1 + 0.001 <=> -0.0011: 1 + -0.001 <=> 0: -1 + -0.001 <=> 0: -1 + -0.001 <=> 0.0011: -1 +-0.001 <=> -0.0011: 1