[gcc r13-7722] libstdc++: Avoid problematic use of log10 in std::format [PR110860]

Jonathan Wakely redi@gcc.gnu.org
Mon Aug 14 17:48:37 GMT 2023


https://gcc.gnu.org/g:559341b5b5a30448362f3f205cd1bf043a919945

commit r13-7722-g559341b5b5a30448362f3f205cd1bf043a919945
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Aug 11 18:10:29 2023 +0100

    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.
    
    (cherry picked from commit 2d2b05f0691799f03062bf5c436462f14cad3e7c)

Diff:
---
 libstdc++-v3/include/std/format | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index fd4cc9205aa2..7091685f1b89 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -1489,12 +1489,21 @@ namespace __format
 	      size_t __guess = 8 + __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;
 		}


More information about the Libstdc++-cvs mailing list