summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZefram <zefram@fysh.org>2018-01-19 05:05:07 +0000
committerZefram <zefram@fysh.org>2018-01-19 05:09:07 +0000
commit60fa46621ae5d0d44c802aedc205274584701fa0 (patch)
tree70bcd377015aeb2dc0bdd0321bbce0b70d7adb3f
parent8c7aa727bdcea40fd6b4ffb9ca8bbf53c0fadb10 (diff)
downloadperl-60fa46621ae5d0d44c802aedc205274584701fa0.tar.gz
fix F0convert() on edge cases
The F0convert() function used to implement the %.0f format specifier more cheaply went wrong on some edge cases. Its rounding went wrong when the exponent is such that fractional values are not representable, making the "+= 0.5" invoke floating point rounding. Fix that by only invoking that rounding logic for values that start out fractional. That fixes the output part of [perl #47602]. It also failed to emit the sign for negative zero. Fix that by making it not apply to zero values.
-rw-r--r--sv.c11
-rw-r--r--t/op/sprintf2.t7
2 files changed, 14 insertions, 4 deletions
diff --git a/sv.c b/sv.c
index 2c13b3b333..6ad33c0bfc 100644
--- a/sv.c
+++ b/sv.c
@@ -11057,12 +11057,15 @@ S_F0convert(NV nv, char *const endbuf, STRLEN *const len)
assert(!Perl_isinfnan(nv));
if (neg)
nv = -nv;
- if (nv < UV_MAX) {
+ if (nv != 0.0 && nv < UV_MAX) {
char *p = endbuf;
- nv += 0.5;
uv = (UV)nv;
- if (uv & 1 && uv == nv)
- uv--; /* Round to even */
+ if (uv != nv) {
+ nv += 0.5;
+ uv = (UV)nv;
+ if (uv & 1 && uv == nv)
+ uv--; /* Round to even */
+ }
do {
const unsigned dig = uv % 10;
*--p = '0' + dig;
diff --git a/t/op/sprintf2.t b/t/op/sprintf2.t
index bf092032bb..5fb75974cb 100644
--- a/t/op/sprintf2.t
+++ b/t/op/sprintf2.t
@@ -1123,5 +1123,12 @@ like sprintf("%p", 0+'NaN'), qr/^[0-9a-f]+$/, "%p and NaN";
"\\x80 in format";
}
+foreach(
+ 0.0, -0.0,
+ 4503599627370501, -4503599627370501,
+ 4503599627370503, -4503599627370503,
+) {
+ is sprintf("%.0f", $_), sprintf("%-.0f", $_), "special-case %.0f on $_";
+}
done_testing();