]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Avoid problematic use of log10 in std::format [PR110860]
authorPaul Dreik <gccpatches@pauldreik.se>
Mon, 14 Aug 2023 14:42:33 +0000 (15:42 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Mon, 14 Aug 2023 17:10:28 +0000 (18:10 +0100)
If abs(__v) is smaller than one, the result will be of the
form 0.xxxxx. It is only if the magnitude is large that more digits
are needed before the decimal dot.

This uses frexp instead of log10 which should be less expensive
and have sufficient precision for the desired purpose.

It removes the problematic cases where log10 will be negative or not
fit in an int.

Signed-off-by: Paul Dreik <gccpatches@pauldreik.se>
libstdc++-v3/ChangeLog:

PR libstdc++/110860
* include/std/format (__formatter_fp::format): Use frexp instead
of log10.

libstdc++-v3/include/std/format

index f4520ff3f746ccea08c4cabf2f8b6c05c514a2b6..7ea0377ec715c95711b108df6eb988b6f3b1c146 100644 (file)
@@ -1490,14 +1490,23 @@ namespace __format
              // If the buffer is too small it's probably because of a large
              // precision, or a very large value in fixed format.
              size_t __guess = 8 + __prec;
-             if (__fmt == chars_format::fixed && __v != 0) // +ddd.prec
+             if (__fmt == chars_format::fixed) // +ddd.prec
                {
-                 if constexpr (is_same_v<_Fp, float>)
-                   __guess += __builtin_log10f(__v < 0.0f ? -__v : __v);
-                 else if constexpr (is_same_v<_Fp, double>)
-                   __guess += __builtin_log10(__v < 0.0 ? -__v : __v);
-                 else if constexpr (is_same_v<_Fp, long double>)
-                   __guess += __builtin_log10l(__v < 0.0l ? -__v : __v);
+                 if constexpr (is_same_v<_Fp, float> || is_same_v<_Fp, double>
+                                 || is_same_v<_Fp, long double>)
+                   {
+                     // The number of digits to the left of the decimal point
+                     // is floor(log10(max(abs(__v),1)))+1
+                     int __exp{};
+                     if constexpr (is_same_v<_Fp, float>)
+                       __builtin_frexpf(__v, &__exp);
+                     else if constexpr (is_same_v<_Fp, double>)
+                       __builtin_frexp(__v, &__exp);
+                     else if constexpr (is_same_v<_Fp, long double>)
+                       __builtin_frexpl(__v, &__exp);
+                     if (__exp > 0)
+                       __guess += 1U + __exp * 4004U / 13301U; // log10(2) approx.
+                   }
                  else
                    __guess += numeric_limits<_Fp>::max_exponent10;
                }