-- bffb14058bb46137d42c7a113a36b6b582997cda by Xiaoyi Zhang <zhangxy@google.com>: Add ABSL_MUST_USE_RESULT to Status. PiperOrigin-RevId: 296272498 -- b426fdd3b3f687d7a8aeb644925923bbab503778 by CJ Johnson <johnsoncj@google.com>: Optimizes absl::InlinedVector::clear() by not deallocating the data, if allocated. This allows allocations to be reused. This matches the behavior of std::vector::clear() PiperOrigin-RevId: 296197235 -- 8cb9fbfe20e749816065c1a042e84f72dac9bfc0 by CJ Johnson <johnsoncj@google.com>: Optimizes absl::InlinedVector::clear() by not deallocating the data, if allocated. This allows allocations to be reused. This matches the behavior of std::vector::clear() PiperOrigin-RevId: 296058092 -- 2558d3369a482879919155b6f46317ccafe0ca13 by Matthew Brown <matthewbr@google.com>: Internal cleanup PiperOrigin-RevId: 296025806 -- cf7ee57228534021c15ed7421df92acf6c27c9c7 by Gennadiy Rozental <rogeeff@google.com>: Make FlagOps enum class. We also add comments to all the functions used to invoke flag ops. PiperOrigin-RevId: 295975809 -- 74bbdbd12fbc54e9c4ebcb3005e727becf0e509d by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Status`. PiperOrigin-RevId: 295777662 -- 3dbc622b4e2227863525da2f7de7ecbeb3ede21f by Xiaoyi Zhang <zhangxy@google.com>: Internal change. PiperOrigin-RevId: 295733658 -- 48d74aa0ab01d611da6012b377f038d8b26c712e by Abseil Team <absl-team@google.com>: Fix typo in container/CMakeLists.txt for container_common PiperOrigin-RevId: 295491438 GitOrigin-RevId: bffb14058bb46137d42c7a113a36b6b582997cda Change-Id: Ia966857b07fa7412cd6489ac37b5fa26640e4141
		
			
				
	
	
		
			493 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			493 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "absl/strings/internal/str_format/float_conversion.h"
 | |
| 
 | |
| #include <string.h>
 | |
| #include <algorithm>
 | |
| #include <cassert>
 | |
| #include <cmath>
 | |
| #include <string>
 | |
| 
 | |
| #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 <typename Float>
 | |
| 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<long double, Float>()) {
 | |
|       *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<size_t>(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<int>(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 <typename Float>
 | |
| 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 <FormatStyle mode>
 | |
| 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 <typename Float, typename Int>
 | |
| 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<Float, long double>::value ||
 | |
|        !std::is_same<Int, uint64_t>::value) &&
 | |
| #endif
 | |
|       std::numeric_limits<Float>::digits <= std::numeric_limits<Int>::digits;
 | |
| }
 | |
| 
 | |
| template <typename Float>
 | |
| struct Decomposed {
 | |
|   Float mantissa;
 | |
|   int exponent;
 | |
| };
 | |
| 
 | |
| // Decompose the double into an integer mantissa and an exponent.
 | |
| template <typename Float>
 | |
| Decomposed<Float> Decompose(Float v) {
 | |
|   int exp;
 | |
|   Float m = std::frexp(v, &exp);
 | |
|   m = std::ldexp(m, std::numeric_limits<Float>::digits);
 | |
|   exp -= std::numeric_limits<Float>::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 <FormatStyle mode, typename Int>
 | |
| 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<FormatStyle::Precision>(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 <typename Int, typename Float, FormatStyle mode>
 | |
| bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
 | |
|                        int *exp_out) {
 | |
|   assert((CanFitMantissa<Float, Int>()));
 | |
| 
 | |
|   const int int_bits = std::numeric_limits<Int>::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<Float>::digits + exp > int_bits) {
 | |
|       // The value will overflow the Int
 | |
|       return false;
 | |
|     }
 | |
|     int digits_printed = PrintIntegralDigits<mode>(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<mode>(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<char>(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>(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<mode>(out, exp_out);
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| template <FormatStyle mode, typename Float>
 | |
| bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out,
 | |
|                    int *exp) {
 | |
|   if (precision > kMaxFixedPrecision) return false;
 | |
| 
 | |
|   // Try with uint64_t.
 | |
|   if (CanFitMantissa<Float, std::uint64_t>() &&
 | |
|       FloatToBufferImpl<std::uint64_t, Float, mode>(
 | |
|           static_cast<std::uint64_t>(decomposed.mantissa),
 | |
|           static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp))
 | |
|     return true;
 | |
| 
 | |
| #if defined(ABSL_HAVE_INTRINSIC_INT128)
 | |
|   // If that is not enough, try with __uint128_t.
 | |
|   return CanFitMantissa<Float, __uint128_t>() &&
 | |
|          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<int>(str.size()) -
 | |
|                                        static_cast<int>(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 <typename Float>
 | |
| 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<FormatStyle::Fixed>(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<FormatStyle::Precision>(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<FormatStyle::Precision>(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
 |