#include "absl/strings/internal/str_format/float_conversion.h" #include #include #include #include #include #include "absl/base/config.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { char *CopyStringTo(string_view v, char *out) { std::memcpy(out, v.data(), v.size()); return out + v.size(); } template bool FallbackToSnprintf(const Float v, const ConversionSpec &conv, FormatSinkImpl *sink) { int w = conv.width() >= 0 ? conv.width() : 0; int p = conv.precision() >= 0 ? conv.precision() : -1; char fmt[32]; { char *fp = fmt; *fp++ = '%'; fp = CopyStringTo(FormatConversionSpecImplFriend::FlagsToString(conv), fp); fp = CopyStringTo("*.*", fp); if (std::is_same()) { *fp++ = 'L'; } *fp++ = FormatConversionCharToChar(conv.conv()); *fp = 0; assert(fp < fmt + sizeof(fmt)); } std::string space(512, '\0'); string_view result; while (true) { int n = snprintf(&space[0], space.size(), fmt, w, p, v); if (n < 0) return false; if (static_cast(n) < space.size()) { result = string_view(space.data(), n); break; } space.resize(n + 1); } sink->Append(result); return true; } // 128-bits in decimal: ceil(128*log(2)/log(10)) // or std::numeric_limits<__uint128_t>::digits10 constexpr int kMaxFixedPrecision = 39; constexpr int kBufferLength = /*sign*/ 1 + /*integer*/ kMaxFixedPrecision + /*point*/ 1 + /*fraction*/ kMaxFixedPrecision + /*exponent e+123*/ 5; struct Buffer { void push_front(char c) { assert(begin > data); *--begin = c; } void push_back(char c) { assert(end < data + sizeof(data)); *end++ = c; } void pop_back() { assert(begin < end); --end; } char &back() { assert(begin < end); return end[-1]; } char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; } int size() const { return static_cast(end - begin); } char data[kBufferLength]; char *begin; char *end; }; enum class FormatStyle { Fixed, Precision }; // If the value is Inf or Nan, print it and return true. // Otherwise, return false. template bool ConvertNonNumericFloats(char sign_char, Float v, const ConversionSpec &conv, FormatSinkImpl *sink) { char text[4], *ptr = text; if (sign_char) *ptr++ = sign_char; if (std::isnan(v)) { ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan", 3, ptr); } else if (std::isinf(v)) { ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf", 3, ptr); } else { return false; } return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, conv.flags().left); } // Round up the last digit of the value. // It will carry over and potentially overflow. 'exp' will be adjusted in that // case. template void RoundUp(Buffer *buffer, int *exp) { char *p = &buffer->back(); while (p >= buffer->begin && (*p == '9' || *p == '.')) { if (*p == '9') *p = '0'; --p; } if (p < buffer->begin) { *p = '1'; buffer->begin = p; if (mode == FormatStyle::Precision) { std::swap(p[1], p[2]); // move the . ++*exp; buffer->pop_back(); } } else { ++*p; } } void PrintExponent(int exp, char e, Buffer *out) { out->push_back(e); if (exp < 0) { out->push_back('-'); exp = -exp; } else { out->push_back('+'); } // Exponent digits. if (exp > 99) { out->push_back(exp / 100 + '0'); out->push_back(exp / 10 % 10 + '0'); out->push_back(exp % 10 + '0'); } else { out->push_back(exp / 10 + '0'); out->push_back(exp % 10 + '0'); } } template constexpr bool CanFitMantissa() { return #if defined(__clang__) && !defined(__SSE3__) // Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289 // Casting from long double to uint64_t is miscompiled and drops bits. (!std::is_same::value || !std::is_same::value) && #endif std::numeric_limits::digits <= std::numeric_limits::digits; } template struct Decomposed { Float mantissa; int exponent; }; // Decompose the double into an integer mantissa and an exponent. template Decomposed Decompose(Float v) { int exp; Float m = std::frexp(v, &exp); m = std::ldexp(m, std::numeric_limits::digits); exp -= std::numeric_limits::digits; return {m, exp}; } // Print 'digits' as decimal. // In Fixed mode, we add a '.' at the end. // In Precision mode, we add a '.' after the first digit. template int PrintIntegralDigits(Int digits, Buffer *out) { int printed = 0; if (digits) { for (; digits; digits /= 10) out->push_front(digits % 10 + '0'); printed = out->size(); if (mode == FormatStyle::Precision) { out->push_front(*out->begin); out->begin[1] = '.'; } else { out->push_back('.'); } } else if (mode == FormatStyle::Fixed) { out->push_front('0'); out->push_back('.'); printed = 1; } return printed; } // Back out 'extra_digits' digits and round up if necessary. bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value, Buffer *out, int *exp_out) { if (extra_digits <= 0) return false; // Back out the extra digits out->end -= extra_digits; bool needs_to_round_up = [&] { // We look at the digit just past the end. // There must be 'extra_digits' extra valid digits after end. if (*out->end > '5') return true; if (*out->end < '5') return false; if (has_leftover_value || std::any_of(out->end + 1, out->end + extra_digits, [](char c) { return c != '0'; })) return true; // Ends in ...50*, round to even. return out->last_digit() % 2 == 1; }(); if (needs_to_round_up) { RoundUp(out, exp_out); } return true; } // Print the value into the buffer. // This will not include the exponent, which will be returned in 'exp_out' for // Precision mode. template bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, int *exp_out) { assert((CanFitMantissa())); const int int_bits = std::numeric_limits::digits; // In precision mode, we start printing one char to the right because it will // also include the '.' // In fixed mode we put the dot afterwards on the right. out->begin = out->end = out->data + 1 + kMaxFixedPrecision + (mode == FormatStyle::Precision); if (exp >= 0) { if (std::numeric_limits::digits + exp > int_bits) { // The value will overflow the Int return false; } int digits_printed = PrintIntegralDigits(int_mantissa << exp, out); int digits_to_zero_pad = precision; if (mode == FormatStyle::Precision) { *exp_out = digits_printed - 1; digits_to_zero_pad -= digits_printed - 1; if (RemoveExtraPrecision(-digits_to_zero_pad, false, out, exp_out)) { return true; } } for (; digits_to_zero_pad-- > 0;) out->push_back('0'); return true; } exp = -exp; // We need at least 4 empty bits for the next decimal digit. // We will multiply by 10. if (exp > int_bits - 4) return false; const Int mask = (Int{1} << exp) - 1; // Print the integral part first. int digits_printed = PrintIntegralDigits(int_mantissa >> exp, out); int_mantissa &= mask; int fractional_count = precision; if (mode == FormatStyle::Precision) { if (digits_printed == 0) { // Find the first non-zero digit, when in Precision mode. *exp_out = 0; if (int_mantissa) { while (int_mantissa <= mask) { int_mantissa *= 10; --*exp_out; } } out->push_front(static_cast(int_mantissa >> exp) + '0'); out->push_back('.'); int_mantissa &= mask; } else { // We already have a digit, and a '.' *exp_out = digits_printed - 1; fractional_count -= *exp_out; if (RemoveExtraPrecision(-fractional_count, int_mantissa != 0, out, exp_out)) { // If we had enough digits, return right away. // The code below will try to round again otherwise. return true; } } } auto get_next_digit = [&] { int_mantissa *= 10; int digit = static_cast(int_mantissa >> exp); int_mantissa &= mask; return digit; }; // Print fractional_count more digits, if available. for (; fractional_count > 0; --fractional_count) { out->push_back(get_next_digit() + '0'); } int next_digit = get_next_digit(); if (next_digit > 5 || (next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) { RoundUp(out, exp_out); } return true; } template bool FloatToBuffer(Decomposed decomposed, int precision, Buffer *out, int *exp) { if (precision > kMaxFixedPrecision) return false; // Try with uint64_t. if (CanFitMantissa() && FloatToBufferImpl( static_cast(decomposed.mantissa), static_cast(decomposed.exponent), precision, out, exp)) return true; #if defined(ABSL_HAVE_INTRINSIC_INT128) // If that is not enough, try with __uint128_t. return CanFitMantissa() && FloatToBufferImpl<__uint128_t, Float, mode>( static_cast<__uint128_t>(decomposed.mantissa), static_cast<__uint128_t>(decomposed.exponent), precision, out, exp); #endif return false; } void WriteBufferToSink(char sign_char, string_view str, const ConversionSpec &conv, FormatSinkImpl *sink) { int left_spaces = 0, zeros = 0, right_spaces = 0; int missing_chars = conv.width() >= 0 ? std::max(conv.width() - static_cast(str.size()) - static_cast(sign_char != 0), 0) : 0; if (conv.flags().left) { right_spaces = missing_chars; } else if (conv.flags().zero) { zeros = missing_chars; } else { left_spaces = missing_chars; } sink->Append(left_spaces, ' '); if (sign_char) sink->Append(1, sign_char); sink->Append(zeros, '0'); sink->Append(str); sink->Append(right_spaces, ' '); } template bool FloatToSink(const Float v, const ConversionSpec &conv, FormatSinkImpl *sink) { // Print the sign or the sign column. Float abs_v = v; char sign_char = 0; if (std::signbit(abs_v)) { sign_char = '-'; abs_v = -abs_v; } else if (conv.flags().show_pos) { sign_char = '+'; } else if (conv.flags().sign_col) { sign_char = ' '; } // Print nan/inf. if (ConvertNonNumericFloats(sign_char, abs_v, conv, sink)) { return true; } int precision = conv.precision() < 0 ? 6 : conv.precision(); int exp = 0; auto decomposed = Decompose(abs_v); Buffer buffer; switch (conv.conv()) { case ConversionChar::f: case ConversionChar::F: if (!FloatToBuffer(decomposed, precision, &buffer, nullptr)) { return FallbackToSnprintf(v, conv, sink); } if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); break; case ConversionChar::e: case ConversionChar::E: if (!FloatToBuffer(decomposed, precision, &buffer, &exp)) { return FallbackToSnprintf(v, conv, sink); } if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', &buffer); break; case ConversionChar::g: case ConversionChar::G: precision = std::max(0, precision - 1); if (!FloatToBuffer(decomposed, precision, &buffer, &exp)) { return FallbackToSnprintf(v, conv, sink); } if (precision + 1 > exp && exp >= -4) { if (exp < 0) { // Have 1.23456, needs 0.00123456 // Move the first digit buffer.begin[1] = *buffer.begin; // Add some zeros for (; exp < -1; ++exp) *buffer.begin-- = '0'; *buffer.begin-- = '.'; *buffer.begin = '0'; } else if (exp > 0) { // Have 1.23456, needs 1234.56 // Move the '.' exp positions to the right. std::rotate(buffer.begin + 1, buffer.begin + 2, buffer.begin + exp + 2); } exp = 0; } if (!conv.flags().alt) { while (buffer.back() == '0') buffer.pop_back(); if (buffer.back() == '.') buffer.pop_back(); } if (exp) { PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', &buffer); } break; case ConversionChar::a: case ConversionChar::A: return FallbackToSnprintf(v, conv, sink); default: return false; } WriteBufferToSink(sign_char, string_view(buffer.begin, buffer.end - buffer.begin), conv, sink); return true; } } // namespace bool ConvertFloatImpl(long double v, const ConversionSpec &conv, FormatSinkImpl *sink) { return FloatToSink(v, conv, sink); } bool ConvertFloatImpl(float v, const ConversionSpec &conv, FormatSinkImpl *sink) { return FloatToSink(v, conv, sink); } bool ConvertFloatImpl(double v, const ConversionSpec &conv, FormatSinkImpl *sink) { return FloatToSink(v, conv, sink); } } // namespace str_format_internal ABSL_NAMESPACE_END } // namespace absl