From f882887178599c1a3a12cb3c7b0f0c3002ae2d45 Mon Sep 17 00:00:00 2001 From: Gabriel Ravier Date: Sun, 8 Sep 2024 03:08:11 +0200 Subject: [PATCH] Fix ecvt/fcvt issues w.r.t. value==0 and ndigit==0 (#1282) Before this commit, cosmopolitan had some issues with handling arguments of 0 and signs, such as returning an incorrect sign when the input value == -0.0, and incorrectly handling ndigit == 0 on fcvt (ndigit determines the amount of digits *after* the radix character on fcvt, thus the parts before it still must be outputted before fcvt's job is completely done). This patch fixes these issues, and adds tests with corresponding inputs. --- libc/stdio/ecvt.c | 14 ++++++++++---- test/libc/stdio/ecvt_test.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/libc/stdio/ecvt.c b/libc/stdio/ecvt.c index ebb72075c..612a66335 100644 --- a/libc/stdio/ecvt.c +++ b/libc/stdio/ecvt.c @@ -22,6 +22,7 @@ │ Materiel Command, USAF, under agreement number F39502-99-1-0512. │ │ SUCH DAMAGE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/math.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -53,8 +54,11 @@ __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) char *p, *rve, c; size_t siz; - if (ndigit == 0) { - *sign = value < 0.0; + // Note that we exclude the case of fmode here, since for fcvt having + // `ndigit == 0` just means we have to output 0 digits *after* the radix + // character + if (ndigit == 0 && !fmode) { + *sign = signbit(value); *decpt = 0; return (""); } @@ -71,10 +75,12 @@ __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) /* __dtoa() doesn't allocate space for 0 so we do it by hand */ if (value == 0.0) { *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */ - *sign = 0; + *sign = signbit(value); if ((rve = s = malloc(siz)) == NULL) return(NULL); - *rve++ = '0'; + // handle fcvt(0, 0, ...) by returning "" + if (siz > 1) + *rve++ = '0'; *rve = '\0'; } else { p = dtoa(value, fmode + 2, ndigit, decpt, sign, &rve); diff --git a/test/libc/stdio/ecvt_test.c b/test/libc/stdio/ecvt_test.c index 2813d8d3c..8806896bb 100644 --- a/test/libc/stdio/ecvt_test.c +++ b/test/libc/stdio/ecvt_test.c @@ -31,3 +31,38 @@ TEST(fcvt, test) { ASSERT_EQ(1, decpt); ASSERT_EQ(0, sign); } + +TEST(ecvt, minus0) { + int decpt = 110000000, sign = 110000000; + + ASSERT_STREQ("00000", ecvt(-0.0, 5, &decpt, &sign)); + ASSERT_LE(0, decpt); + ASSERT_GE(1, decpt); + ASSERT_EQ(1, sign); +} + +TEST(ecvt, minus0ndigits0) { + int decpt = 110000000, sign = 110000000; + + ASSERT_STREQ("", ecvt(-0.0, 0, &decpt, &sign)); + ASSERT_LE(0, decpt); + ASSERT_GE(1, decpt); + ASSERT_EQ(1, sign); +} + +TEST(fcvt, ndigits0) { + int decpt = 110000000, sign = 110000000; + + ASSERT_STREQ("1", fcvt(0.6, 0, &decpt, &sign)); + ASSERT_EQ(1, decpt); + ASSERT_EQ(0, sign); +} + +TEST(fcvt, minus0ndigits0) { + int decpt = 110000000, sign = 110000000; + + ASSERT_STREQ("", fcvt(-0.0, 0, &decpt, &sign)); + ASSERT_LE(0, decpt); + ASSERT_GE(1, decpt); + ASSERT_EQ(1, sign); +}