aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Dreik <gccpatches@pauldreik.se>2023-08-14 15:42:33 +0100
committerJonathan Wakely <jwakely@redhat.com>2023-08-14 18:10:28 +0100
commit2d2b05f0691799f03062bf5c436462f14cad3e7c (patch)
tree856b1ac16da30f801ee2e9511339ebca003fbced
parent882af290c137dfab5d99b88e6dbecc5e75d85a0b (diff)
downloadgcc-2d2b05f0691799f03062bf5c436462f14cad3e7c.zip
gcc-2d2b05f0691799f03062bf5c436462f14cad3e7c.tar.gz
gcc-2d2b05f0691799f03062bf5c436462f14cad3e7c.tar.bz2
libstdc++: Avoid problematic use of log10 in std::format [PR110860]
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.
-rw-r--r--libstdc++-v3/include/std/format23
1 files changed, 16 insertions, 7 deletions
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index f4520ff..7ea0377 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -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;
}