[committed] libstdc++: Fix incorrect use of abs and log10 in std::format [PR110860]

Message ID 20230807211428.701867-1-jwakely@redhat.com
State Accepted
Headers
Series [committed] libstdc++: Fix incorrect use of abs and log10 in std::format [PR110860] |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Jonathan Wakely Aug. 7, 2023, 9:14 p.m. UTC
  Tested x86_64-linux. Pushed to trunk. gcc-13 backport to follow.

-- >8 --

The std::formatter implementation for floating-point types uses
__builtin_abs and __builtin_log10 to avoid including all of <cmath>, but
those functions are not generic. The result of abs(2e304) is -INT_MIN
which is undefined, and then log10(INT_MIN) is NaN. As well as being
undefined, we fail to grow the buffer correctly, and then loop more
times than needed to allocate a buffer and try formatting the value into
it again.

We can use if-constexpr to choose the correct form of log10 to use for
the type, and avoid using abs entirely. This avoids the undefined
behaviour and should mean we only reallocate and retry std::to_chars
once.

libstdc++-v3/ChangeLog:

	PR libstdc++/110860
	* include/std/format (__formatter_fp::format): Do not use
	__builtin_abs and __builtin_log10 with arbitrary floating-point
	types.
---
 libstdc++-v3/include/std/format | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)
  

Patch

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 60e53642b11..f68308e7210 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -1487,9 +1487,20 @@  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 =  __prec + sizeof(__buf);
-	      if (__fmt == chars_format::fixed)
-		__guess += max((int)__builtin_log10(__builtin_abs(__v)) / 2, 1);
+	      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);
+		  else
+		    __guess += numeric_limits<_Fp>::max_exponent10;
+		}
+	      if (__guess <= sizeof(__buf)) [[unlikely]]
+		__guess = sizeof(__buf) * 2;
 	      __dynbuf.reserve(__guess);
 
 	      do