summaryrefslogtreecommitdiff
path: root/absl/strings/internal/str_format/convert_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/internal/str_format/convert_test.cc')
-rw-r--r--absl/strings/internal/str_format/convert_test.cc245
1 files changed, 217 insertions, 28 deletions
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index cbcd7caf..dd167f76 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -1,14 +1,18 @@
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
+
#include <cctype>
#include <cmath>
+#include <limits>
#include <string>
+#include <thread> // NOLINT
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/strings/internal/str_format/bind.h"
+#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -57,7 +61,7 @@ std::string Esc(const T &v) {
return oss.str();
}
-void StrAppend(std::string *dst, const char *format, va_list ap) {
+void StrAppendV(std::string *dst, const char *format, va_list ap) {
// First try with a small fixed size buffer
static const int kSpaceLength = 1024;
char space[kSpaceLength];
@@ -98,11 +102,18 @@ void StrAppend(std::string *dst, const char *format, va_list ap) {
delete[] buf;
}
+void StrAppend(std::string *out, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StrAppendV(out, format, ap);
+ va_end(ap);
+}
+
std::string StrPrint(const char *format, ...) {
va_list ap;
va_start(ap, format);
std::string result;
- StrAppend(&result, format, ap);
+ StrAppendV(&result, format, ap);
va_end(ap);
return result;
}
@@ -471,8 +482,8 @@ TEST_F(FormatConvertTest, Float) {
#endif // _MSC_VER
const char *const kFormats[] = {
- "%", "%.3", "%8.5", "%9", "%.60", "%.30", "%03", "%+",
- "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"};
+ "%", "%.3", "%8.5", "%500", "%.5000", "%.60", "%.30", "%03",
+ "%+", "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"};
std::vector<double> doubles = {0.0,
-0.0,
@@ -489,11 +500,6 @@ TEST_F(FormatConvertTest, Float) {
std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity()};
-#ifndef __APPLE__
- // Apple formats NaN differently (+nan) vs. (nan)
- doubles.push_back(std::nan(""));
-#endif
-
// Some regression tests.
doubles.push_back(0.99999999999999989);
@@ -512,43 +518,204 @@ TEST_F(FormatConvertTest, Float) {
}
}
+ // Workaround libc bug.
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=22142
+ const bool gcc_bug_22142 =
+ StrPrint("%f", std::numeric_limits<double>::max()) !=
+ "1797693134862315708145274237317043567980705675258449965989174768031"
+ "5726078002853876058955863276687817154045895351438246423432132688946"
+ "4182768467546703537516986049910576551282076245490090389328944075868"
+ "5084551339423045832369032229481658085593321233482747978262041447231"
+ "68738177180919299881250404026184124858368.000000";
+
+ if (!gcc_bug_22142) {
+ for (int exp = -300; exp <= 300; ++exp) {
+ const double all_ones_mantissa = 0x1fffffffffffff;
+ doubles.push_back(std::ldexp(all_ones_mantissa, exp));
+ }
+ }
+
+ if (gcc_bug_22142) {
+ for (auto &d : doubles) {
+ using L = std::numeric_limits<double>;
+ double d2 = std::abs(d);
+ if (d2 == L::max() || d2 == L::min() || d2 == L::denorm_min()) {
+ d = 0;
+ }
+ }
+ }
+
+ // Remove duplicates to speed up the logic below.
+ std::sort(doubles.begin(), doubles.end());
+ doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end());
+
+#ifndef __APPLE__
+ // Apple formats NaN differently (+nan) vs. (nan)
+ doubles.push_back(std::nan(""));
+#endif
+
+ // Reserve the space to ensure we don't allocate memory in the output itself.
+ std::string str_format_result;
+ str_format_result.reserve(1 << 20);
+ std::string string_printf_result;
+ string_printf_result.reserve(1 << 20);
+
for (const char *fmt : kFormats) {
for (char f : {'f', 'F', //
'g', 'G', //
'a', 'A', //
'e', 'E'}) {
std::string fmt_str = std::string(fmt) + f;
+
+ if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') {
+ // This particular test takes way too long with snprintf.
+ // Disable for the case we are not implementing natively.
+ continue;
+ }
+
for (double d : doubles) {
int i = -10;
FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
UntypedFormatSpecImpl format(fmt_str);
- // We use ASSERT_EQ here because failures are usually correlated and a
- // bug would print way too many failed expectations causing the test to
- // time out.
- ASSERT_EQ(StrPrint(fmt_str.c_str(), d, i),
- FormatPack(format, absl::MakeSpan(args)))
- << fmt_str << " " << StrPrint("%.18g", d) << " "
- << StrPrint("%.999f", d);
+
+ string_printf_result.clear();
+ StrAppend(&string_printf_result, fmt_str.c_str(), d, i);
+ str_format_result.clear();
+
+ {
+ AppendPack(&str_format_result, format, absl::MakeSpan(args));
+ }
+
+ if (string_printf_result != str_format_result) {
+ // We use ASSERT_EQ here because failures are usually correlated and a
+ // bug would print way too many failed expectations causing the test
+ // to time out.
+ ASSERT_EQ(string_printf_result, str_format_result)
+ << fmt_str << " " << StrPrint("%.18g", d) << " "
+ << StrPrint("%a", d) << " " << StrPrint("%.1080f", d);
+ }
}
}
}
}
+TEST_F(FormatConvertTest, FloatRound) {
+ std::string s;
+ const auto format = [&](const char *fmt, double d) -> std::string & {
+ s.clear();
+ FormatArgImpl args[1] = {FormatArgImpl(d)};
+ AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
+#if !defined(_MSC_VER)
+ // MSVC has a different rounding policy than us so we can't test our
+ // implementation against the native one there.
+ EXPECT_EQ(StrPrint(fmt, d), s);
+#endif // _MSC_VER
+
+ return s;
+ };
+ // All of these values have to be exactly represented.
+ // Otherwise we might not be testing what we think we are testing.
+
+ // These values can fit in a 64bit "fast" representation.
+ const double exact_value = 0.00000000000005684341886080801486968994140625;
+ assert(exact_value == std::pow(2, -44));
+ // Round up at a 5xx.
+ EXPECT_EQ(format("%.13f", exact_value), "0.0000000000001");
+ // Round up at a >5
+ EXPECT_EQ(format("%.14f", exact_value), "0.00000000000006");
+ // Round down at a <5
+ EXPECT_EQ(format("%.16f", exact_value), "0.0000000000000568");
+ // Nine handling
+ EXPECT_EQ(format("%.35f", exact_value),
+ "0.00000000000005684341886080801486969");
+ EXPECT_EQ(format("%.36f", exact_value),
+ "0.000000000000056843418860808014869690");
+ // Round down the last nine.
+ EXPECT_EQ(format("%.37f", exact_value),
+ "0.0000000000000568434188608080148696899");
+ EXPECT_EQ(format("%.10f", 0.000003814697265625), "0.0000038147");
+ // Round up the last nine
+ EXPECT_EQ(format("%.11f", 0.000003814697265625), "0.00000381470");
+ EXPECT_EQ(format("%.12f", 0.000003814697265625), "0.000003814697");
+
+ // Round to even (down)
+ EXPECT_EQ(format("%.43f", exact_value),
+ "0.0000000000000568434188608080148696899414062");
+ // Exact
+ EXPECT_EQ(format("%.44f", exact_value),
+ "0.00000000000005684341886080801486968994140625");
+ // Round to even (up), let make the last digits 75 instead of 25
+ EXPECT_EQ(format("%.43f", exact_value + std::pow(2, -43)),
+ "0.0000000000001705302565824240446090698242188");
+ // Exact, just to check.
+ EXPECT_EQ(format("%.44f", exact_value + std::pow(2, -43)),
+ "0.00000000000017053025658242404460906982421875");
+
+ // This value has to be small enough that it won't fit in the uint128
+ // representation for printing.
+ const double small_exact_value =
+ 0.000000000000000000000000000000000000752316384526264005099991383822237233803945956334136013765601092018187046051025390625; // NOLINT
+ assert(small_exact_value == std::pow(2, -120));
+ // Round up at a 5xx.
+ EXPECT_EQ(format("%.37f", small_exact_value),
+ "0.0000000000000000000000000000000000008");
+ // Round down at a <5
+ EXPECT_EQ(format("%.38f", small_exact_value),
+ "0.00000000000000000000000000000000000075");
+ // Round up at a >5
+ EXPECT_EQ(format("%.41f", small_exact_value),
+ "0.00000000000000000000000000000000000075232");
+ // Nine handling
+ EXPECT_EQ(format("%.55f", small_exact_value),
+ "0.0000000000000000000000000000000000007523163845262640051");
+ EXPECT_EQ(format("%.56f", small_exact_value),
+ "0.00000000000000000000000000000000000075231638452626400510");
+ EXPECT_EQ(format("%.57f", small_exact_value),
+ "0.000000000000000000000000000000000000752316384526264005100");
+ EXPECT_EQ(format("%.58f", small_exact_value),
+ "0.0000000000000000000000000000000000007523163845262640051000");
+ // Round down the last nine
+ EXPECT_EQ(format("%.59f", small_exact_value),
+ "0.00000000000000000000000000000000000075231638452626400509999");
+ // Round up the last nine
+ EXPECT_EQ(format("%.79f", small_exact_value),
+ "0.000000000000000000000000000000000000"
+ "7523163845262640050999913838222372338039460");
+
+ // Round to even (down)
+ EXPECT_EQ(format("%.119f", small_exact_value),
+ "0.000000000000000000000000000000000000"
+ "75231638452626400509999138382223723380"
+ "394595633413601376560109201818704605102539062");
+ // Exact
+ EXPECT_EQ(format("%.120f", small_exact_value),
+ "0.000000000000000000000000000000000000"
+ "75231638452626400509999138382223723380"
+ "3945956334136013765601092018187046051025390625");
+ // Round to even (up), let make the last digits 75 instead of 25
+ EXPECT_EQ(format("%.119f", small_exact_value + std::pow(2, -119)),
+ "0.000000000000000000000000000000000002"
+ "25694915357879201529997415146671170141"
+ "183786900240804129680327605456113815307617188");
+ // Exact, just to check.
+ EXPECT_EQ(format("%.120f", small_exact_value + std::pow(2, -119)),
+ "0.000000000000000000000000000000000002"
+ "25694915357879201529997415146671170141"
+ "1837869002408041296803276054561138153076171875");
+}
+
TEST_F(FormatConvertTest, LongDouble) {
- const char *const kFormats[] = {"%", "%.3", "%8.5", "%9",
+#ifdef _MSC_VER
+ // MSVC has a different rounding policy than us so we can't test our
+ // implementation against the native one there.
+ return;
+#endif // _MSC_VER
+ const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000",
"%.60", "%+", "% ", "%-10"};
- // This value is not representable in double, but it is in long double that
- // uses the extended format.
- // This is to verify that we are not truncating the value mistakenly through a
- // double.
- long double very_precise = 10000000000000000.25L;
-
std::vector<long double> doubles = {
0.0,
-0.0,
- very_precise,
- 1 / very_precise,
std::numeric_limits<long double>::max(),
-std::numeric_limits<long double>::max(),
std::numeric_limits<long double>::min(),
@@ -556,22 +723,44 @@ TEST_F(FormatConvertTest, LongDouble) {
std::numeric_limits<long double>::infinity(),
-std::numeric_limits<long double>::infinity()};
+ for (long double base : {1.L, 12.L, 123.L, 1234.L, 12345.L, 123456.L,
+ 1234567.L, 12345678.L, 123456789.L, 1234567890.L,
+ 12345678901.L, 123456789012.L, 1234567890123.L,
+ // This value is not representable in double, but it
+ // is in long double that uses the extended format.
+ // This is to verify that we are not truncating the
+ // value mistakenly through a double.
+ 10000000000000000.25L}) {
+ for (int exp : {-1000, -500, 0, 500, 1000}) {
+ for (int sign : {1, -1}) {
+ doubles.push_back(sign * std::ldexp(base, exp));
+ doubles.push_back(sign / std::ldexp(base, exp));
+ }
+ }
+ }
+
for (const char *fmt : kFormats) {
for (char f : {'f', 'F', //
'g', 'G', //
'a', 'A', //
'e', 'E'}) {
std::string fmt_str = std::string(fmt) + 'L' + f;
+
+ if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') {
+ // This particular test takes way too long with snprintf.
+ // Disable for the case we are not implementing natively.
+ continue;
+ }
+
for (auto d : doubles) {
FormatArgImpl arg(d);
UntypedFormatSpecImpl format(fmt_str);
// We use ASSERT_EQ here because failures are usually correlated and a
// bug would print way too many failed expectations causing the test to
// time out.
- ASSERT_EQ(StrPrint(fmt_str.c_str(), d),
- FormatPack(format, {&arg, 1}))
+ ASSERT_EQ(StrPrint(fmt_str.c_str(), d), FormatPack(format, {&arg, 1}))
<< fmt_str << " " << StrPrint("%.18Lg", d) << " "
- << StrPrint("%.999Lf", d);
+ << StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d);
}
}
}