diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index b0fbfcc89099f..298a07ae47a77 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -266,6 +266,19 @@ php_sprintf_appenddouble(zend_string **buffer, size_t *pos, s = php_conv_fp((fmt == 'f')?'F':fmt, number, 0, precision, (fmt == 'f')?LCONV_DECIMAL_POINT:'.', &is_negative, &num_buf[1], &s_len); + /* Prevent negative sign when value rounds to zero (GH-12237) */ + if (is_negative) { + double rounded; + if (fmt == 'f' || fmt == 'F') { + double factor = pow(10.0, (double)precision); + rounded = round(fabs(number) * factor) / factor; + } else { + rounded = fabs(number); + } + if (rounded == 0.0) { + is_negative = false; + } + } if (is_negative) { num_buf[0] = '-'; s = num_buf; diff --git a/ext/standard/tests/strings/gh12237.phpt b/ext/standard/tests/strings/gh12237.phpt new file mode 100644 index 0000000000000..652070a50d92c --- /dev/null +++ b/ext/standard/tests/strings/gh12237.phpt @@ -0,0 +1,107 @@ +--TEST-- +GH-12237 (printf/sprintf should not produce negative zero) +--FILE-- + +--EXPECT-- +-- Basic case -- +string(4) "0.00" +string(4) "0.00" +-- Precisions rounding to zero -- +string(1) "0" +string(1) "0" +string(3) "0.0" +string(3) "0.0" +string(4) "0.00" +string(4) "0.00" +string(5) "0.000" +string(5) "0.000" +string(6) "0.0000" +string(6) "0.0000" +-- Should stay negative -- +string(5) "-0.01" +string(5) "-0.10" +string(5) "-1.50" +string(4) "-0.1" +string(2) "-1" +-- Uppercase F -- +string(4) "0.00" +string(5) "-0.01" +-- Explicit sign -- +string(5) "+0.00" +string(5) "+0.00" +string(5) "-0.01" +-- printf -- +0.00 +-0.01 +-- number_format consistency -- +string(4) "0.00" +string(5) "-0.01" +-- Very small negatives -- +string(4) "0.00" +string(4) "0.00" +string(4) "0.00" +-- Padding -- +string(8) "00000.00" +string(8) "-0000.01" diff --git a/ext/standard/tests/strings/sprintf_variation9.phpt b/ext/standard/tests/strings/sprintf_variation9.phpt index e2f4d113245ab..3394d4a4e9fa3 100644 --- a/ext/standard/tests/strings/sprintf_variation9.phpt +++ b/ext/standard/tests/strings/sprintf_variation9.phpt @@ -336,15 +336,15 @@ string(8) "0.000000" string(30) " 0.000000" -- Iteration 26 -- -string(9) "-0.000000" -string(9) "-0.000000" -string(10) " -0.000000" -string(10) "-0.000000 " -string(10) " -0.000000" -string(10) " --0.000000" -string(9) "-0.000000" -string(30) " -0.000000" +string(8) "0.000000" +string(8) "0.000000" +string(9) " 0.000000" +string(9) "0.000000 " +string(9) " 0.000000" +string(9) " +0.000000" +string(8) "0.000000" +string(30) " 0.000000" -- Iteration 27 -- string(50) "5000000000000000069686058479707049565356032.000000" @@ -380,13 +380,13 @@ string(8) "0.000000" string(30) " 0.000000" -- Iteration 30 -- -string(9) "-0.000000" -string(9) "-0.000000" -string(10) " -0.000000" -string(10) "-0.000000 " -string(10) " -0.000000" -string(10) " --0.000000" -string(9) "-0.000000" -string(30) " -0.000000" +string(8) "0.000000" +string(8) "0.000000" +string(9) " 0.000000" +string(9) "0.000000 " +string(9) " 0.000000" +string(9) " +0.000000" +string(8) "0.000000" +string(30) " 0.000000" Done diff --git a/ext/standard/tests/strings/vprintf_variation5.phpt b/ext/standard/tests/strings/vprintf_variation5.phpt index b09acb7877396..50c66c02fde1b 100644 --- a/ext/standard/tests/strings/vprintf_variation5.phpt +++ b/ext/standard/tests/strings/vprintf_variation5.phpt @@ -64,8 +64,8 @@ int(28) int(54) -- Iteration 4 -- -200000.0000 0.0000 -200000.000000 -0.0000 -int(45) +200000.0000 0.0000 -200000.000000 0.0000 +int(44) -- Iteration 5 -- 20000.000000 -1999999999999999879418332743206357172224.000000 0.000000 20000000000000000000.000000