• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "absl/strings/internal/str_format/float_conversion.h"
2 
3 #include <string.h>
4 #include <algorithm>
5 #include <cassert>
6 #include <cmath>
7 #include <string>
8 
9 #include "absl/base/config.h"
10 
11 namespace absl {
12 ABSL_NAMESPACE_BEGIN
13 namespace str_format_internal {
14 
15 namespace {
16 
CopyStringTo(string_view v,char * out)17 char *CopyStringTo(string_view v, char *out) {
18   std::memcpy(out, v.data(), v.size());
19   return out + v.size();
20 }
21 
22 template <typename Float>
FallbackToSnprintf(const Float v,const ConversionSpec & conv,FormatSinkImpl * sink)23 bool FallbackToSnprintf(const Float v, const ConversionSpec &conv,
24                         FormatSinkImpl *sink) {
25   int w = conv.width() >= 0 ? conv.width() : 0;
26   int p = conv.precision() >= 0 ? conv.precision() : -1;
27   char fmt[32];
28   {
29     char *fp = fmt;
30     *fp++ = '%';
31     fp = CopyStringTo(FormatConversionSpecImplFriend::FlagsToString(conv), fp);
32     fp = CopyStringTo("*.*", fp);
33     if (std::is_same<long double, Float>()) {
34       *fp++ = 'L';
35     }
36     *fp++ = FormatConversionCharToChar(conv.conv());
37     *fp = 0;
38     assert(fp < fmt + sizeof(fmt));
39   }
40   std::string space(512, '\0');
41   string_view result;
42   while (true) {
43     int n = snprintf(&space[0], space.size(), fmt, w, p, v);
44     if (n < 0) return false;
45     if (static_cast<size_t>(n) < space.size()) {
46       result = string_view(space.data(), n);
47       break;
48     }
49     space.resize(n + 1);
50   }
51   sink->Append(result);
52   return true;
53 }
54 
55 // 128-bits in decimal: ceil(128*log(2)/log(10))
56 //   or std::numeric_limits<__uint128_t>::digits10
57 constexpr int kMaxFixedPrecision = 39;
58 
59 constexpr int kBufferLength = /*sign*/ 1 +
60                               /*integer*/ kMaxFixedPrecision +
61                               /*point*/ 1 +
62                               /*fraction*/ kMaxFixedPrecision +
63                               /*exponent e+123*/ 5;
64 
65 struct Buffer {
push_frontabsl::str_format_internal::__anonc8c866bc0111::Buffer66   void push_front(char c) {
67     assert(begin > data);
68     *--begin = c;
69   }
push_backabsl::str_format_internal::__anonc8c866bc0111::Buffer70   void push_back(char c) {
71     assert(end < data + sizeof(data));
72     *end++ = c;
73   }
pop_backabsl::str_format_internal::__anonc8c866bc0111::Buffer74   void pop_back() {
75     assert(begin < end);
76     --end;
77   }
78 
backabsl::str_format_internal::__anonc8c866bc0111::Buffer79   char &back() {
80     assert(begin < end);
81     return end[-1];
82   }
83 
last_digitabsl::str_format_internal::__anonc8c866bc0111::Buffer84   char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; }
85 
sizeabsl::str_format_internal::__anonc8c866bc0111::Buffer86   int size() const { return static_cast<int>(end - begin); }
87 
88   char data[kBufferLength];
89   char *begin;
90   char *end;
91 };
92 
93 enum class FormatStyle { Fixed, Precision };
94 
95 // If the value is Inf or Nan, print it and return true.
96 // Otherwise, return false.
97 template <typename Float>
ConvertNonNumericFloats(char sign_char,Float v,const ConversionSpec & conv,FormatSinkImpl * sink)98 bool ConvertNonNumericFloats(char sign_char, Float v,
99                              const ConversionSpec &conv, FormatSinkImpl *sink) {
100   char text[4], *ptr = text;
101   if (sign_char) *ptr++ = sign_char;
102   if (std::isnan(v)) {
103     ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan",
104                       3, ptr);
105   } else if (std::isinf(v)) {
106     ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf",
107                       3, ptr);
108   } else {
109     return false;
110   }
111 
112   return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1,
113                                conv.flags().left);
114 }
115 
116 // Round up the last digit of the value.
117 // It will carry over and potentially overflow. 'exp' will be adjusted in that
118 // case.
119 template <FormatStyle mode>
RoundUp(Buffer * buffer,int * exp)120 void RoundUp(Buffer *buffer, int *exp) {
121   char *p = &buffer->back();
122   while (p >= buffer->begin && (*p == '9' || *p == '.')) {
123     if (*p == '9') *p = '0';
124     --p;
125   }
126 
127   if (p < buffer->begin) {
128     *p = '1';
129     buffer->begin = p;
130     if (mode == FormatStyle::Precision) {
131       std::swap(p[1], p[2]);  // move the .
132       ++*exp;
133       buffer->pop_back();
134     }
135   } else {
136     ++*p;
137   }
138 }
139 
PrintExponent(int exp,char e,Buffer * out)140 void PrintExponent(int exp, char e, Buffer *out) {
141   out->push_back(e);
142   if (exp < 0) {
143     out->push_back('-');
144     exp = -exp;
145   } else {
146     out->push_back('+');
147   }
148   // Exponent digits.
149   if (exp > 99) {
150     out->push_back(exp / 100 + '0');
151     out->push_back(exp / 10 % 10 + '0');
152     out->push_back(exp % 10 + '0');
153   } else {
154     out->push_back(exp / 10 + '0');
155     out->push_back(exp % 10 + '0');
156   }
157 }
158 
159 template <typename Float, typename Int>
CanFitMantissa()160 constexpr bool CanFitMantissa() {
161   return
162 #if defined(__clang__) && !defined(__SSE3__)
163       // Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289
164       // Casting from long double to uint64_t is miscompiled and drops bits.
165       (!std::is_same<Float, long double>::value ||
166        !std::is_same<Int, uint64_t>::value) &&
167 #endif
168       std::numeric_limits<Float>::digits <= std::numeric_limits<Int>::digits;
169 }
170 
171 template <typename Float>
172 struct Decomposed {
173   Float mantissa;
174   int exponent;
175 };
176 
177 // Decompose the double into an integer mantissa and an exponent.
178 template <typename Float>
Decompose(Float v)179 Decomposed<Float> Decompose(Float v) {
180   int exp;
181   Float m = std::frexp(v, &exp);
182   m = std::ldexp(m, std::numeric_limits<Float>::digits);
183   exp -= std::numeric_limits<Float>::digits;
184   return {m, exp};
185 }
186 
187 // Print 'digits' as decimal.
188 // In Fixed mode, we add a '.' at the end.
189 // In Precision mode, we add a '.' after the first digit.
190 template <FormatStyle mode, typename Int>
PrintIntegralDigits(Int digits,Buffer * out)191 int PrintIntegralDigits(Int digits, Buffer *out) {
192   int printed = 0;
193   if (digits) {
194     for (; digits; digits /= 10) out->push_front(digits % 10 + '0');
195     printed = out->size();
196     if (mode == FormatStyle::Precision) {
197       out->push_front(*out->begin);
198       out->begin[1] = '.';
199     } else {
200       out->push_back('.');
201     }
202   } else if (mode == FormatStyle::Fixed) {
203     out->push_front('0');
204     out->push_back('.');
205     printed = 1;
206   }
207   return printed;
208 }
209 
210 // Back out 'extra_digits' digits and round up if necessary.
RemoveExtraPrecision(int extra_digits,bool has_leftover_value,Buffer * out,int * exp_out)211 bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value,
212                           Buffer *out, int *exp_out) {
213   if (extra_digits <= 0) return false;
214 
215   // Back out the extra digits
216   out->end -= extra_digits;
217 
218   bool needs_to_round_up = [&] {
219     // We look at the digit just past the end.
220     // There must be 'extra_digits' extra valid digits after end.
221     if (*out->end > '5') return true;
222     if (*out->end < '5') return false;
223     if (has_leftover_value || std::any_of(out->end + 1, out->end + extra_digits,
224                                           [](char c) { return c != '0'; }))
225       return true;
226 
227     // Ends in ...50*, round to even.
228     return out->last_digit() % 2 == 1;
229   }();
230 
231   if (needs_to_round_up) {
232     RoundUp<FormatStyle::Precision>(out, exp_out);
233   }
234   return true;
235 }
236 
237 // Print the value into the buffer.
238 // This will not include the exponent, which will be returned in 'exp_out' for
239 // Precision mode.
240 template <typename Int, typename Float, FormatStyle mode>
FloatToBufferImpl(Int int_mantissa,int exp,int precision,Buffer * out,int * exp_out)241 bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
242                        int *exp_out) {
243   assert((CanFitMantissa<Float, Int>()));
244 
245   const int int_bits = std::numeric_limits<Int>::digits;
246 
247   // In precision mode, we start printing one char to the right because it will
248   // also include the '.'
249   // In fixed mode we put the dot afterwards on the right.
250   out->begin = out->end =
251       out->data + 1 + kMaxFixedPrecision + (mode == FormatStyle::Precision);
252 
253   if (exp >= 0) {
254     if (std::numeric_limits<Float>::digits + exp > int_bits) {
255       // The value will overflow the Int
256       return false;
257     }
258     int digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out);
259     int digits_to_zero_pad = precision;
260     if (mode == FormatStyle::Precision) {
261       *exp_out = digits_printed - 1;
262       digits_to_zero_pad -= digits_printed - 1;
263       if (RemoveExtraPrecision(-digits_to_zero_pad, false, out, exp_out)) {
264         return true;
265       }
266     }
267     for (; digits_to_zero_pad-- > 0;) out->push_back('0');
268     return true;
269   }
270 
271   exp = -exp;
272   // We need at least 4 empty bits for the next decimal digit.
273   // We will multiply by 10.
274   if (exp > int_bits - 4) return false;
275 
276   const Int mask = (Int{1} << exp) - 1;
277 
278   // Print the integral part first.
279   int digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out);
280   int_mantissa &= mask;
281 
282   int fractional_count = precision;
283   if (mode == FormatStyle::Precision) {
284     if (digits_printed == 0) {
285       // Find the first non-zero digit, when in Precision mode.
286       *exp_out = 0;
287       if (int_mantissa) {
288         while (int_mantissa <= mask) {
289           int_mantissa *= 10;
290           --*exp_out;
291         }
292       }
293       out->push_front(static_cast<char>(int_mantissa >> exp) + '0');
294       out->push_back('.');
295       int_mantissa &= mask;
296     } else {
297       // We already have a digit, and a '.'
298       *exp_out = digits_printed - 1;
299       fractional_count -= *exp_out;
300       if (RemoveExtraPrecision(-fractional_count, int_mantissa != 0, out,
301                                exp_out)) {
302         // If we had enough digits, return right away.
303         // The code below will try to round again otherwise.
304         return true;
305       }
306     }
307   }
308 
309   auto get_next_digit = [&] {
310     int_mantissa *= 10;
311     int digit = static_cast<int>(int_mantissa >> exp);
312     int_mantissa &= mask;
313     return digit;
314   };
315 
316   // Print fractional_count more digits, if available.
317   for (; fractional_count > 0; --fractional_count) {
318     out->push_back(get_next_digit() + '0');
319   }
320 
321   int next_digit = get_next_digit();
322   if (next_digit > 5 ||
323       (next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) {
324     RoundUp<mode>(out, exp_out);
325   }
326 
327   return true;
328 }
329 
330 template <FormatStyle mode, typename Float>
FloatToBuffer(Decomposed<Float> decomposed,int precision,Buffer * out,int * exp)331 bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out,
332                    int *exp) {
333   if (precision > kMaxFixedPrecision) return false;
334 
335   // Try with uint64_t.
336   if (CanFitMantissa<Float, std::uint64_t>() &&
337       FloatToBufferImpl<std::uint64_t, Float, mode>(
338           static_cast<std::uint64_t>(decomposed.mantissa),
339           static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp))
340     return true;
341 
342 #if defined(ABSL_HAVE_INTRINSIC_INT128)
343   // If that is not enough, try with __uint128_t.
344   return CanFitMantissa<Float, __uint128_t>() &&
345          FloatToBufferImpl<__uint128_t, Float, mode>(
346              static_cast<__uint128_t>(decomposed.mantissa),
347              static_cast<__uint128_t>(decomposed.exponent), precision, out,
348              exp);
349 #endif
350   return false;
351 }
352 
WriteBufferToSink(char sign_char,string_view str,const ConversionSpec & conv,FormatSinkImpl * sink)353 void WriteBufferToSink(char sign_char, string_view str,
354                        const ConversionSpec &conv, FormatSinkImpl *sink) {
355   int left_spaces = 0, zeros = 0, right_spaces = 0;
356   int missing_chars =
357       conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) -
358                                        static_cast<int>(sign_char != 0),
359                                    0)
360                         : 0;
361   if (conv.flags().left) {
362     right_spaces = missing_chars;
363   } else if (conv.flags().zero) {
364     zeros = missing_chars;
365   } else {
366     left_spaces = missing_chars;
367   }
368 
369   sink->Append(left_spaces, ' ');
370   if (sign_char) sink->Append(1, sign_char);
371   sink->Append(zeros, '0');
372   sink->Append(str);
373   sink->Append(right_spaces, ' ');
374 }
375 
376 template <typename Float>
FloatToSink(const Float v,const ConversionSpec & conv,FormatSinkImpl * sink)377 bool FloatToSink(const Float v, const ConversionSpec &conv,
378                  FormatSinkImpl *sink) {
379   // Print the sign or the sign column.
380   Float abs_v = v;
381   char sign_char = 0;
382   if (std::signbit(abs_v)) {
383     sign_char = '-';
384     abs_v = -abs_v;
385   } else if (conv.flags().show_pos) {
386     sign_char = '+';
387   } else if (conv.flags().sign_col) {
388     sign_char = ' ';
389   }
390 
391   // Print nan/inf.
392   if (ConvertNonNumericFloats(sign_char, abs_v, conv, sink)) {
393     return true;
394   }
395 
396   int precision = conv.precision() < 0 ? 6 : conv.precision();
397 
398   int exp = 0;
399 
400   auto decomposed = Decompose(abs_v);
401 
402   Buffer buffer;
403 
404   switch (conv.conv()) {
405     case ConversionChar::f:
406     case ConversionChar::F:
407       if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer,
408                                              nullptr)) {
409         return FallbackToSnprintf(v, conv, sink);
410       }
411       if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back();
412       break;
413 
414     case ConversionChar::e:
415     case ConversionChar::E:
416       if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
417                                                  &exp)) {
418         return FallbackToSnprintf(v, conv, sink);
419       }
420       if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back();
421       PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e',
422                     &buffer);
423       break;
424 
425     case ConversionChar::g:
426     case ConversionChar::G:
427       precision = std::max(0, precision - 1);
428       if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
429                                                  &exp)) {
430         return FallbackToSnprintf(v, conv, sink);
431       }
432       if (precision + 1 > exp && exp >= -4) {
433         if (exp < 0) {
434           // Have 1.23456, needs 0.00123456
435           // Move the first digit
436           buffer.begin[1] = *buffer.begin;
437           // Add some zeros
438           for (; exp < -1; ++exp) *buffer.begin-- = '0';
439           *buffer.begin-- = '.';
440           *buffer.begin = '0';
441         } else if (exp > 0) {
442           // Have 1.23456, needs 1234.56
443           // Move the '.' exp positions to the right.
444           std::rotate(buffer.begin + 1, buffer.begin + 2,
445                       buffer.begin + exp + 2);
446         }
447         exp = 0;
448       }
449       if (!conv.flags().alt) {
450         while (buffer.back() == '0') buffer.pop_back();
451         if (buffer.back() == '.') buffer.pop_back();
452       }
453       if (exp) {
454         PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e',
455                       &buffer);
456       }
457       break;
458 
459     case ConversionChar::a:
460     case ConversionChar::A:
461       return FallbackToSnprintf(v, conv, sink);
462 
463     default:
464       return false;
465   }
466 
467   WriteBufferToSink(sign_char,
468                     string_view(buffer.begin, buffer.end - buffer.begin), conv,
469                     sink);
470 
471   return true;
472 }
473 
474 }  // namespace
475 
ConvertFloatImpl(long double v,const ConversionSpec & conv,FormatSinkImpl * sink)476 bool ConvertFloatImpl(long double v, const ConversionSpec &conv,
477                       FormatSinkImpl *sink) {
478   return FloatToSink(v, conv, sink);
479 }
480 
ConvertFloatImpl(float v,const ConversionSpec & conv,FormatSinkImpl * sink)481 bool ConvertFloatImpl(float v, const ConversionSpec &conv,
482                       FormatSinkImpl *sink) {
483   return FloatToSink(v, conv, sink);
484 }
485 
ConvertFloatImpl(double v,const ConversionSpec & conv,FormatSinkImpl * sink)486 bool ConvertFloatImpl(double v, const ConversionSpec &conv,
487                       FormatSinkImpl *sink) {
488   return FloatToSink(v, conv, sink);
489 }
490 
491 }  // namespace str_format_internal
492 ABSL_NAMESPACE_END
493 }  // namespace absl
494