summaryrefslogtreecommitdiff
path: root/absl/strings/internal
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/internal')
-rw-r--r--absl/strings/internal/str_format/convert_test.cc88
-rw-r--r--absl/strings/internal/str_format/float_conversion.cc33
2 files changed, 63 insertions, 58 deletions
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 375db0a0..926283cf 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -554,7 +554,8 @@ TEST_F(FormatConvertTest, Uint128) {
}
template <typename Floating>
-void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats) {
+void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats,
+ const std::set<Floating> &skip_verify) {
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
// Reserve the space to ensure we don't allocate memory in the output itself.
std::string str_format_result;
@@ -602,7 +603,16 @@ void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats) {
AppendPack(&str_format_result, format, absl::MakeSpan(args));
}
- if (string_printf_result != str_format_result) {
+#ifdef _MSC_VER
+ // MSVC has a different rounding policy than us so we can't test our
+ // implementation against the native one there.
+ continue;
+#elif defined(__APPLE__)
+ // Apple formats NaN differently (+nan) vs. (nan)
+ if (std::isnan(d)) continue;
+#endif
+ if (string_printf_result != str_format_result &&
+ skip_verify.find(d) == skip_verify.end()) {
// 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.
@@ -616,12 +626,6 @@ void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats) {
}
TEST_F(FormatConvertTest, Float) {
-#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
-
std::vector<float> floats = {0.0f,
-0.0f,
.9999999f,
@@ -635,7 +639,8 @@ TEST_F(FormatConvertTest, Float) {
std::numeric_limits<float>::epsilon(),
std::numeric_limits<float>::epsilon() + 1.0f,
std::numeric_limits<float>::infinity(),
- -std::numeric_limits<float>::infinity()};
+ -std::numeric_limits<float>::infinity(),
+ std::nanf("")};
// Some regression tests.
floats.push_back(0.999999989f);
@@ -664,21 +669,14 @@ TEST_F(FormatConvertTest, Float) {
std::sort(floats.begin(), floats.end());
floats.erase(std::unique(floats.begin(), floats.end()), floats.end());
-#ifndef __APPLE__
- // Apple formats NaN differently (+nan) vs. (nan)
- floats.push_back(std::nan(""));
-#endif
-
- TestWithMultipleFormatsHelper(floats);
+ TestWithMultipleFormatsHelper(floats, {});
}
TEST_F(FormatConvertTest, Double) {
-#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
-
+ // For values that we know won't match the standard library implementation we
+ // skip verification, but still run the algorithm to catch asserts/sanitizer
+ // bugs.
+ std::set<double> skip_verify;
std::vector<double> doubles = {0.0,
-0.0,
.99999999999999,
@@ -692,7 +690,8 @@ TEST_F(FormatConvertTest, Double) {
std::numeric_limits<double>::epsilon(),
std::numeric_limits<double>::epsilon() + 1,
std::numeric_limits<double>::infinity(),
- -std::numeric_limits<double>::infinity()};
+ -std::numeric_limits<double>::infinity(),
+ std::nan("")};
// Some regression tests.
doubles.push_back(0.99999999999999989);
@@ -722,33 +721,29 @@ TEST_F(FormatConvertTest, Double) {
"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));
+ 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) {
+ skip_verify.insert(doubles.back());
}
}
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;
- }
- }
+ using L = std::numeric_limits<double>;
+ skip_verify.insert(L::max());
+ skip_verify.insert(L::min()); // NOLINT
+ skip_verify.insert(L::denorm_min());
+ skip_verify.insert(-L::max());
+ skip_verify.insert(-L::min()); // NOLINT
+ skip_verify.insert(-L::denorm_min());
}
// 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
-
- TestWithMultipleFormatsHelper(doubles);
+ TestWithMultipleFormatsHelper(doubles, skip_verify);
}
TEST_F(FormatConvertTest, DoubleRound) {
@@ -1069,11 +1064,6 @@ TEST_F(FormatConvertTest, ExtremeWidthPrecision) {
}
TEST_F(FormatConvertTest, LongDouble) {
-#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 NativePrintfTraits &native_traits = VerifyNativeImplementation();
const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000",
"%.60", "%+", "% ", "%-10"};
@@ -1134,10 +1124,18 @@ TEST_F(FormatConvertTest, LongDouble) {
for (auto d : doubles) {
FormatArgImpl arg(d);
UntypedFormatSpecImpl format(fmt_str);
+ std::string result = FormatPack(format, {&arg, 1});
+
+#ifdef _MSC_VER
+ // MSVC has a different rounding policy than us so we can't test our
+ // implementation against the native one there.
+ continue;
+#endif // _MSC_VER
+
// 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), result)
<< fmt_str << " " << StrPrint("%.18Lg", d) << " "
<< StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d);
}
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc
index 2aa41aa7..2b1fd8cb 100644
--- a/absl/strings/internal/str_format/float_conversion.cc
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -112,12 +112,22 @@ inline uint64_t DivideBy10WithCarry(uint64_t *v, uint64_t carry) {
return next_carry % divisor;
}
+constexpr bool IsDoubleDouble() {
+ // This is the `double-double` representation of `long double`.
+ // We do not handle it natively. Fallback to snprintf.
+ return std::numeric_limits<long double>::digits ==
+ 2 * std::numeric_limits<double>::digits;
+}
+
+using MaxFloatType =
+ typename std::conditional<IsDoubleDouble(), double, long double>::type;
+
// Generates the decimal representation for an integer of the form `v * 2^exp`,
// where `v` and `exp` are both positive integers.
// It generates the digits from the left (ie the most significant digit first)
// to allow for direct printing into the sink.
//
-// Requires `0 <= exp` and `exp <= numeric_limits<long double>::max_exponent`.
+// Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`.
class BinaryToDecimal {
static constexpr int ChunksNeeded(int exp) {
// We will left shift a uint128 by `exp` bits, so we need `128+exp` total
@@ -132,10 +142,10 @@ class BinaryToDecimal {
static void RunConversion(uint128 v, int exp,
absl::FunctionRef<void(BinaryToDecimal)> f) {
assert(exp > 0);
- assert(exp <= std::numeric_limits<long double>::max_exponent);
+ assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent);
static_assert(
static_cast<int>(StackArray::kMaxCapacity) >=
- ChunksNeeded(std::numeric_limits<long double>::max_exponent),
+ ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent),
"");
StackArray::RunWithCapacity(
@@ -232,14 +242,14 @@ class BinaryToDecimal {
// Converts a value of the form `x * 2^-exp` into a sequence of decimal digits.
// Requires `-exp < 0` and
-// `-exp >= limits<long double>::min_exponent - limits<long double>::digits`.
+// `-exp >= limits<MaxFloatType>::min_exponent - limits<MaxFloatType>::digits`.
class FractionalDigitGenerator {
public:
// Run the conversion for `v * 2^exp` and call `f(generator)`.
// This function will allocate enough stack space to perform the conversion.
static void RunConversion(
uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) {
- using Limits = std::numeric_limits<long double>;
+ using Limits = std::numeric_limits<MaxFloatType>;
assert(-exp < 0);
assert(-exp >= Limits::min_exponent - 128);
static_assert(StackArray::kMaxCapacity >=
@@ -871,10 +881,10 @@ void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp,
// This buffer holds the "0x1.ab1de3" portion of "0x1.ab1de3pe+2". Compute the
// size with long double which is the largest of the floats.
constexpr size_t kBufSizeForHexFloatRepr =
- 2 // 0x
- + std::numeric_limits<long double>::digits / 4 // number of hex digits
- + 1 // round up
- + 1; // "." (dot)
+ 2 // 0x
+ + std::numeric_limits<MaxFloatType>::digits / 4 // number of hex digits
+ + 1 // round up
+ + 1; // "." (dot)
char digits_buffer[kBufSizeForHexFloatRepr];
char *digits_iter = digits_buffer;
const char *const digits =
@@ -1393,10 +1403,7 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv,
bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv,
FormatSinkImpl *sink) {
- if (std::numeric_limits<long double>::digits ==
- 2 * std::numeric_limits<double>::digits) {
- // This is the `double-double` representation of `long double`.
- // We do not handle it natively. Fallback to snprintf.
+ if (IsDoubleDouble()) {
return FallbackToSnprintf(v, conv, sink);
}