From 39bf8afa4bacc0861a2692e4417983aded29d12d Mon Sep 17 00:00:00 2001 From: lacatoire Date: Sat, 21 Feb 2026 23:34:10 +0100 Subject: [PATCH 1/4] Fixed GH-12237: sprintf/printf no longer produces negative zero When a negative floating-point value rounds to zero at the requested precision, sprintf/printf now outputs "0.00" instead of "-0.00". This aligns the behavior of sprintf() with number_format(), which already suppresses the negative sign for values that round to zero. --- ext/standard/formatted_print.c | 13 +++ ext/standard/tests/strings/gh12237.phpt | 107 ++++++++++++++++++ .../tests/strings/sprintf_variation9.phpt | 36 +++--- 3 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 ext/standard/tests/strings/gh12237.phpt 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 From ebc98215e596c9799089e3ba3b9376a6f164282c Mon Sep 17 00:00:00 2001 From: Louis-Arnaud Date: Sun, 22 Feb 2026 08:22:43 +0100 Subject: [PATCH 2/4] Update formatted_print.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Voříšek --- ext/standard/formatted_print.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index 298a07ae47a77..1175516564b7c 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -268,13 +268,6 @@ php_sprintf_appenddouble(zend_string **buffer, size_t *pos, &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; } From 85ffb7a498108e11d6ae2cdf5b9193e4169ba881 Mon Sep 17 00:00:00 2001 From: lacatoire Date: Mon, 23 Feb 2026 09:28:19 +0100 Subject: [PATCH 3/4] Restore rounded variable declaration removed by mistake The previous commit (suggestion from GitHub UI) accidentally removed the declaration and computation of the 'rounded' variable while keeping its usage, causing a compilation error. --- ext/standard/formatted_print.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index 1175516564b7c..298a07ae47a77 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -268,6 +268,13 @@ php_sprintf_appenddouble(zend_string **buffer, size_t *pos, &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; } From ae65bd503f8c5d6e55d0e1596793f96260a1154a Mon Sep 17 00:00:00 2001 From: lacatoire Date: Mon, 23 Feb 2026 10:23:49 +0100 Subject: [PATCH 4/4] Update vprintf_variation5 test expectation for negative zero fix The GH-12237 fix correctly removes the negative sign when the value rounds to zero. Update the expected output for -2e-5 formatted with %04.4f: -0.0000 becomes 0.0000. --- ext/standard/tests/strings/vprintf_variation5.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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