aboutsummaryrefslogtreecommitdiff
path: root/libc/test/src/stdio/sprintf_test.cpp
diff options
context:
space:
mode:
authorMichael Jones <michaelrj@google.com>2023-08-09 11:38:09 -0700
committerMichael Jones <michaelrj@google.com>2023-08-15 16:23:19 -0700
commitcd06b9d98fe23ffbebe8d730ccf4d2a51f373849 (patch)
tree5383d7f7086f9196287fa667fb24bcd1025fc603 /libc/test/src/stdio/sprintf_test.cpp
parentb02e73a3551f7bd9c852e599e4a323f84e4fe43a (diff)
downloadllvm-cd06b9d98fe23ffbebe8d730ccf4d2a51f373849.zip
llvm-cd06b9d98fe23ffbebe8d730ccf4d2a51f373849.tar.gz
llvm-cd06b9d98fe23ffbebe8d730ccf4d2a51f373849.tar.bz2
[libc] Fix printf %e and %g bugs
Fuzzing revealed bugs in the %e and %g conversions. Since these are very similar, they are grouped together. Again, most of the bugs were related to rounding. As an example, previously the code to check if the number was truncated only worked for digits below the decimal point, due to it being originally designed for %f. This patch adds a mechanism to check the digits above the decimal point for both %e and %g. Reviewed By: sivachandra, lntue Differential Revision: https://reviews.llvm.org/D157536
Diffstat (limited to 'libc/test/src/stdio/sprintf_test.cpp')
-rw-r--r--libc/test/src/stdio/sprintf_test.cpp69
1 files changed, 67 insertions, 2 deletions
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 8739038..42aa85b 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -888,7 +888,8 @@ TEST_F(LlvmLibcSPrintfTest, FloatDecimalConv) {
double inf = __llvm_libc::fputil::FPBits<double>::inf().get_val();
double nan = __llvm_libc::fputil::FPBits<double>::build_nan(1);
- char big_buff[10000];
+ char big_buff[10000]; // Used for long doubles and other extremely wide
+ // numbers.
written = __llvm_libc::sprintf(buff, "%f", 1.0);
ASSERT_STREQ_LEN(written, buff, "1.000000");
@@ -1696,6 +1697,14 @@ TEST_F(LlvmLibcSPrintfTest, FloatExponentConv) {
// Length Modifier Tests.
+#if defined(SPECIAL_X86_LONG_DOUBLE)
+ written = __llvm_libc::sprintf(buff, "%.9Le", 1000000000500000000.1L);
+ ASSERT_STREQ_LEN(written, buff, "1.000000001e+18");
+
+ written = __llvm_libc::sprintf(buff, "%.9Le", 1000000000500000000.0L);
+ ASSERT_STREQ_LEN(written, buff, "1.000000000e+18");
+#endif
+
// TODO: Fix long doubles (needs bigger table or alternate algorithm.)
// Currently the table values are generated, which is very slow.
/*
@@ -1908,6 +1917,45 @@ TEST_F(LlvmLibcSPrintfTest, FloatExponentConv) {
written = __llvm_libc::sprintf(buff, "%.5e", 1.008e3);
ASSERT_STREQ_LEN(written, buff, "1.00800e+03");
+ // These tests also focus on rounding. Almost all of them have a 5 right after
+ // the printed string (e.g. 9.5 with precision 0 prints 0 digits after the
+ // decimal point). This is again because rounding a number with a 5 after the
+ // printed section means that more digits have to be checked to determine if
+ // this should be rounded up (if there are non-zero digits after the 5) or to
+ // even (if the 5 is the last non-zero digit). Additionally, the algorithm for
+ // checking if a number is all 0s after the decimal point may not work since
+ // the decimal point moves in this representation.
+ written = __llvm_libc::sprintf(buff, "%.0e", 2.5812229360061737E+200);
+ ASSERT_STREQ_LEN(written, buff, "3e+200");
+
+ written = __llvm_libc::sprintf(buff, "%.1e", 9.059E+200);
+ ASSERT_STREQ_LEN(written, buff, "9.1e+200");
+
+ written = __llvm_libc::sprintf(buff, "%.0e", 9.059E+200);
+ ASSERT_STREQ_LEN(written, buff, "9e+200");
+
+ written = __llvm_libc::sprintf(buff, "%.166e", 1.131959884853339E-72);
+ ASSERT_STREQ_LEN(written, buff,
+ "1."
+ "13195988485333904593863991136097397258531639976739227369782"
+ "68612419376648241056393424414314951197624317440549121097287"
+ "069853416091591569170304865110665559768676757812e-72");
+
+ written = __llvm_libc::sprintf(buff, "%.0e", 9.5);
+ ASSERT_STREQ_LEN(written, buff, "1e+01");
+
+ written = __llvm_libc::sprintf(buff, "%.10e", 1.9999999999890936);
+ ASSERT_STREQ_LEN(written, buff, "2.0000000000e+00");
+
+ written = __llvm_libc::sprintf(buff, "%.1e", 745362143563.03894);
+ ASSERT_STREQ_LEN(written, buff, "7.5e+11");
+
+ written = __llvm_libc::sprintf(buff, "%.0e", 45181042688.0);
+ ASSERT_STREQ_LEN(written, buff, "5e+10");
+
+ written = __llvm_libc::sprintf(buff, "%.35e", 1.3752441369139243);
+ ASSERT_STREQ_LEN(written, buff, "1.37524413691392433101157166674965993e+00");
+
// Subnormal Precision Tests
written = __llvm_libc::sprintf(buff, "%.310e", 0x1.0p-1022);
@@ -2512,8 +2560,25 @@ TEST_F(LlvmLibcSPrintfTest, FloatAutoConv) {
written = __llvm_libc::sprintf(buff, "%.15g", 22.25);
ASSERT_STREQ_LEN(written, buff, "22.25");
- // Subnormal Precision Tests
+ // These tests also focus on rounding, but only in how it relates to the base
+ // 10 exponent. The %g conversion selects between being a %f or %e conversion
+ // based on what the exponent would be if it was %e. If we call the precision
+ // P (equal to 6 if the precision is not set, 0 if the provided precision is
+ // 0, and provided precision - 1 otherwise) and the exponent X, then the style
+ // is %f with an effective precision of P - X + 1 if P > X >= -4, else the
+ // style is %e with effective precision P - 1. Additionally, it attempts to
+ // trim zeros that would be displayed after the decimal point.
+ written = __llvm_libc::sprintf(buff, "%.1g", 9.059E+200);
+ ASSERT_STREQ_LEN(written, buff, "9e+200");
+
+ written = __llvm_libc::sprintf(buff, "%.2g", 9.059E+200);
+ ASSERT_STREQ_LEN(written, buff, "9.1e+200");
+ // For this test, P = 0 and X = 1, so P > X >= -4 is false, giving a %e style.
+ written = __llvm_libc::sprintf(buff, "%.0g", 9.5);
+ ASSERT_STREQ_LEN(written, buff, "1e+01");
+
+ // Subnormal Precision Tests
written = __llvm_libc::sprintf(buff, "%.310g", 0x1.0p-1022);
ASSERT_STREQ_LEN(
written, buff,