[gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] Add std::to_chars implementation for float and double

Patrick Palka ppalka@gcc.gnu.org
Fri Jul 10 21:26:09 GMT 2020


https://gcc.gnu.org/g:3aff3280de3bc548d670d061467ed86fc119edc3

commit 3aff3280de3bc548d670d061467ed86fc119edc3
Author: Patrick Palka <ppalka@redhat.com>
Date:   Fri Jul 10 16:20:09 2020 -0400

    Add std::to_chars implementation for float and double
    
    libstdc++-v3/ChangeLog:
    
            * acinclude.m4 (libtool_VERSION): Bump to 6:29:0.
            * config/abi/pre/gnu.ver: Add new exports.
            * configure: Regenerate.
            * include/std/charconv (to_chars): Declare the overloads for
            float and double.
            * src/c++17/Makefile.am (sources): Add floating_to_chars.cc.
            * src/c++17/Makefile.in: Regenerate.
            * src/c++17/floating_to_chars.cc: New file.
            * testsuite/util/testsuite_abi.cc: Add new symbol version.

Diff:
---
 libstdc++-v3/acinclude.m4                    |   2 +-
 libstdc++-v3/config/abi/pre/gnu.ver          |   8 +
 libstdc++-v3/configure                       |   2 +-
 libstdc++-v3/include/std/charconv            |  13 +
 libstdc++-v3/src/c++17/Makefile.am           |   1 +
 libstdc++-v3/src/c++17/Makefile.in           |   5 +-
 libstdc++-v3/src/c++17/floating_to_chars.cc  | 918 +++++++++++++++++++++++++++
 libstdc++-v3/testsuite/util/testsuite_abi.cc |   3 +-
 8 files changed, 947 insertions(+), 5 deletions(-)

diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index ee5e0336f2c..e3926e1c9c2 100644
--- a/libstdc++-v3/acinclude.m4
+++ b/libstdc++-v3/acinclude.m4
@@ -3846,7 +3846,7 @@ changequote([,])dnl
 fi
 
 # For libtool versioning info, format is CURRENT:REVISION:AGE
-libtool_VERSION=6:28:0
+libtool_VERSION=6:29:0
 
 # Everything parsed; figure out what files and settings to use.
 case $enable_symvers in
diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index edf4485e607..14f77ddd902 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2299,6 +2299,14 @@ GLIBCXX_3.4.28 {
 
 } GLIBCXX_3.4.27;
 
+GLIBCXX_3.4.29 {
+    # floating-point std::to_chars
+    _ZSt8to_charsPcS_[fd];
+    _ZSt8to_charsPcS_[fd]St12chars_format;
+    _ZSt8to_charsPcS_[fd]St12chars_formati;
+
+} GLIBCXX_3.4.28;
+
 # Symbols in the support library (libsupc++) have their own tag.
 CXXABI_1.3 {
 
diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure
index dd54bd406a9..73f771e7335 100755
--- a/libstdc++-v3/configure
+++ b/libstdc++-v3/configure
@@ -75231,7 +75231,7 @@ $as_echo "$as_me: WARNING: === Symbol versioning will be disabled." >&2;}
 fi
 
 # For libtool versioning info, format is CURRENT:REVISION:AGE
-libtool_VERSION=6:28:0
+libtool_VERSION=6:29:0
 
 # Everything parsed; figure out what files and settings to use.
 case $enable_symvers in
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index cc7dd0e3758..e1dc11bed87 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -688,6 +688,19 @@ namespace __detail
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
+  to_chars_result to_chars(char* __first, char* __last, float __value) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, double __value) noexcept;
+
+  to_chars_result to_chars(char* __first, char* __last, float __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, double __value,
+			   chars_format __fmt) noexcept;
+
+  to_chars_result to_chars(char* __first, char* __last, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 #endif // C++14
diff --git a/libstdc++-v3/src/c++17/Makefile.am b/libstdc++-v3/src/c++17/Makefile.am
index 85e31fbd91f..6637c5b4500 100644
--- a/libstdc++-v3/src/c++17/Makefile.am
+++ b/libstdc++-v3/src/c++17/Makefile.am
@@ -50,6 +50,7 @@ inst_sources =
 endif
 
 sources = \
+	floating_to_chars.cc \
 	fs_dir.cc \
 	fs_ops.cc \
 	fs_path.cc \
diff --git a/libstdc++-v3/src/c++17/Makefile.in b/libstdc++-v3/src/c++17/Makefile.in
index de605a3f6a6..3b3b0d93a2c 100644
--- a/libstdc++-v3/src/c++17/Makefile.in
+++ b/libstdc++-v3/src/c++17/Makefile.in
@@ -124,8 +124,8 @@ LTLIBRARIES = $(noinst_LTLIBRARIES)
 libc__17convenience_la_LIBADD =
 @ENABLE_DUAL_ABI_TRUE@am__objects_1 = cow-fs_dir.lo cow-fs_ops.lo \
 @ENABLE_DUAL_ABI_TRUE@	cow-fs_path.lo
-am__objects_2 = fs_dir.lo fs_ops.lo fs_path.lo memory_resource.lo \
-	$(am__objects_1)
+am__objects_2 = floating_to_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
+	memory_resource.lo $(am__objects_1)
 @ENABLE_DUAL_ABI_TRUE@am__objects_3 = cow-string-inst.lo
 @ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_4 = ostream-inst.lo \
 @ENABLE_EXTERN_TEMPLATE_TRUE@	string-inst.lo $(am__objects_3)
@@ -435,6 +435,7 @@ headers =
 @ENABLE_EXTERN_TEMPLATE_TRUE@	$(extra_string_inst_sources)
 
 sources = \
+	floating_to_chars.cc \
 	fs_dir.cc \
 	fs_ops.cc \
 	fs_path.cc \
diff --git a/libstdc++-v3/src/c++17/floating_to_chars.cc b/libstdc++-v3/src/c++17/floating_to_chars.cc
new file mode 100644
index 00000000000..8e90fdab176
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,918 @@
+// floating-point std::to_chars implementation -*- C++ -*-
+
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+// Activate __glibcxx_assert within this file.
+#define _GLIBCXX_DEBUG
+
+#include <charconv>
+
+#include <cassert>
+#include <cmath>
+#include <cstring>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+namespace
+{
+namespace __ryu
+{
+#include "ryu/common.h"
+#include "ryu/digit_table.h"
+#include "ryu/d2s_intrinsics.h"
+#include "ryu/d2s_full_table.h"
+#include "ryu/d2fixed_full_table.h"
+#include "ryu/f2s_intrinsics.h"
+
+#include "ryu/d2s.c"
+#include "ryu/d2fixed.c"
+#include "ryu/f2s.c"
+} // namespace __ryu
+} // anon namespace
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  // This subroutine of __floating_to_chars_* handles writing nan, inf and 0 in
+  // all formatting modes.
+  template<typename T>
+  static optional<to_chars_result>
+  __handle_special_value(char* first, char* const last, const T value,
+			 const chars_format fmt, const int precision)
+  {
+    __glibcxx_assert(precision >= 0);
+
+    string_view str;
+    switch (fpclassify(value))
+      {
+      case FP_INFINITE:
+	str = "-inf";
+	break;
+
+      case FP_NAN:
+	str = "-nan";
+	break;
+
+      case FP_ZERO:
+	break;
+
+      default:
+      case FP_SUBNORMAL:
+      case FP_NORMAL: [[likely]]
+	return nullopt;
+      }
+
+    if (!str.empty()) [[unlikely]]
+      {
+	if (!signbit(value))
+	  str.remove_prefix(1);
+
+	if (last - first < (int)str.length())
+	  return {{last, errc::value_too_large}};
+
+	memcpy(first, &str[0], str.length());
+	first += str.length();
+	return {{first, errc{}}};
+      }
+
+    __glibcxx_assert(value == 0);
+    const bool neg_zero_p = signbit(value);
+    int output_length;
+    switch (fmt)
+      {
+      case chars_format::fixed:
+      case chars_format::scientific:
+      case chars_format::hex:
+	output_length = neg_zero_p + 1;
+	if (precision)
+	  output_length += strlen(".") + precision;
+	if (fmt == chars_format::scientific)
+	  output_length += strlen("e+00");
+	else if (fmt == chars_format::hex)
+	  output_length += strlen("p+0");
+	if (last - first < output_length)
+	  return {{last, errc::value_too_large}};
+
+	if (neg_zero_p)
+	  *first++ = '-';
+	*first++ = '0';
+	if (precision)
+	  {
+	    *first++ = '.';
+	    memset(first, '0', precision);
+	    first += precision;
+	  }
+	if (fmt == chars_format::scientific)
+	  {
+	    memcpy(first, "e+00", 4);
+	    first += 4;
+	  }
+	else if (fmt == chars_format::hex)
+	  {
+	    memcpy(first, "p+0", 3);
+	    first += 3;
+	  }
+	return {{first, errc{}}};
+
+      default:
+	__glibcxx_assert(fmt == chars_format::general || fmt == chars_format{});
+	output_length = neg_zero_p + 1;
+	if (last - first < output_length)
+	  return {{last, errc::value_too_large}};
+
+	if (neg_zero_p)
+	  *first++ = '-';
+	*first++ = '0';
+	return {{first, errc{}}};
+      }
+  }
+
+  // This subroutine returns true if the shortest scientific form fd is a
+  // positive power of 10, and the floating-point number that has this shortest
+  // scientific form is smaller than this power of 10.
+  //
+  // For instance, the exactly-representable 64-bit number
+  // 99999999999999991611392. has the shortest scientific form 1e23, so its
+  // exact value is smaller than its shortest scientific form.
+  //
+  // Usually we can trust the shortest scientific exponent to determine the
+  // length of the exact value, but for these powers of 10 the length of the
+  // exact value is one smaller than what the scientific exponent suggests.
+  //
+  // This subroutine inspects a lookup table to detect when fd is such a
+  // "special" power of 10.
+  static bool
+  __is_special_pow10_for_fixed_p(__ryu::floating_decimal_64 fd)
+  {
+    if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+      return false;
+
+    static const uint64_t pow10_adjustment_tab[]
+      = { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	  0b0111100011110101011000011110000000110110010101011000001110011111,
+	  0b0101101100000000011100100100111100110110110100010001010101110000,
+	  0b0011110010111000101111110101100011101100010001010000000101100111,
+	  0b0001010000011001011100100001010000010101101000001101000000000000, };
+    return pow10_adjustment_tab[fd.exponent/64] & (1ull<<(63-fd.exponent%64));
+  }
+
+  // Similar to the above, but with 32-bit floats.
+  static bool
+  __is_special_pow10_for_fixed_p(__ryu::floating_decimal_32 fd)
+  {
+    if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+      return false;
+
+    static const uint32_t pow10_adjustment_tab[]
+      = { 0b00000000000111010111001101011001,
+	  0b01101110000000000000000000000000 };
+    return pow10_adjustment_tab[fd.exponent/32] & (1ull<<(31-fd.exponent%32));
+  }
+
+  namespace
+  {
+    // A traits class that contains pertinent information about the binary
+    // representation of each of the floating-point types we support.
+    template<typename T>
+      struct __floating_type_traits
+      { };
+
+    template<>
+      struct __floating_type_traits<float>
+      {
+	using uint_t = uint32_t;
+	static constexpr int mantissa_bits = __FLT_MANT_DIG__ - 1;
+	static constexpr int exponent_bits
+	  = sizeof(float)*__CHAR_BIT__ - mantissa_bits - 1;
+      };
+
+    template<>
+      struct __floating_type_traits<double>
+      {
+	using uint_t = uint64_t;
+	static constexpr int mantissa_bits = __DBL_MANT_DIG__ - 1;
+	static constexpr int exponent_bits
+	  = sizeof(double)*__CHAR_BIT__ - mantissa_bits - 1;
+      };
+  } // anon namespace
+
+  // This subroutine of the floating-point to_chars overloads performs
+  // hexadecimal formatting.  It is parameterized by the floating-point type T.
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_hex(char* first, char* last, const T value,
+			    std::optional<int> precision)
+    {
+      __glibcxx_requires_valid_range(first, last);
+
+      constexpr int mantissa_bits = __floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = __floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using uint_t = typename __floating_type_traits<T>::uint_t;
+
+      constexpr unsigned full_hex_precision = (mantissa_bits + 3) / 4;
+      if (precision && *precision < 0)
+	*precision = full_hex_precision;
+
+      if (auto result = __handle_special_value(first, last, value,
+					       chars_format::hex,
+					       precision.value_or(0)))
+	return *result;
+
+      // Extract the sign, mantissa and exponent from the value.
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(T));
+      const bool sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      const uint_t ieee_mantissa = value_bits & ((1ull << mantissa_bits) - 1u);
+      const uint32_t biased_exponent
+	= (value_bits >> mantissa_bits) & ((1ull << exponent_bits) - 1u);
+      const bool is_normal_number = (biased_exponent != 0);
+
+      // Calculate the unbiased exponent.
+      const int32_t unbiased_exponent = (is_normal_number
+					 ? biased_exponent - exponent_bias
+					 : 1 - exponent_bias);
+
+      // Shift the mantissa so that its bitwidth is a multiple of 4.
+      constexpr unsigned rounded_mantissa_bits = (mantissa_bits + 3) / 4 * 4;
+      uint_t augmented_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	// Restore the mantissa's implicit leading bit.
+	augmented_mantissa |= 1ull << rounded_mantissa_bits;
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      const int trailing_zeros = __builtin_ctzll(augmented_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      const int effective_precision = precision.value_or(shortest_full_precision);
+      if (effective_precision < shortest_full_precision)
+	{
+	  // When limiting the precision, we need to determine how to round the
+	  // least significant printed hexit.  The following branchless
+	  // bit-level-parallel technique computes whether to round up the
+	  // mantissa bit at index N (according to round-to-nearest rules) when
+	  // dropping N bits of precision, for each index N in the bit vector.
+	  // This technique is borrowed from the MSVC implementation.
+	  using bitvec = uint_t;
+	  const bitvec round_bit = augmented_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = augmented_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  if (should_round & (1ull << dropped_bits))
+	    // Round up the least significant printed hexit.
+	    augmented_mantissa += 1ull << dropped_bits;
+	  // Mask out the dropped nibbles.
+	  augmented_mantissa >>= dropped_bits,
+	  augmented_mantissa <<= dropped_bits;
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      const unsigned nibble = augmented_mantissa >> rounded_mantissa_bits;
+      __glibcxx_assert(nibble <= 2);
+      const char leading_hexit = '0' + nibble;
+      augmented_mantissa &= (1ull << rounded_mantissa_bits) - 1;
+
+      // Now before we start writing the string, determine the total length of
+      // the output string and perform a single bounds check.
+      int expected_output_length = sign + 1;
+      if (effective_precision != 0)
+	expected_output_length += strlen(".") + effective_precision;
+      const int abs_exponent = (unbiased_exponent < 0
+				? -unbiased_exponent : unbiased_exponent);
+      int exponent_length;
+      if (abs_exponent >= 1000)
+	exponent_length = 4;
+      else if (abs_exponent >= 100)
+	exponent_length = 3;
+      else if (abs_exponent >= 10)
+	exponent_length = 2;
+      else
+	exponent_length = 1;
+      expected_output_length += strlen("p+") + exponent_length;
+      if (last - first < expected_output_length)
+	return {last, errc::value_too_large};
+
+      const auto saved_first [[maybe_unused]] = first;
+      // Write the negative sign and the leading hexit.
+      if (sign)
+	*first++ = '-';
+      *first++ = leading_hexit;
+
+      if (effective_precision > 0)
+	{
+	  *first++ = '.';
+	  int written_hexits = 0;
+	  // Extract and mask out the leading nibble after the decimal point,
+	  // write its corresponding hexit, and repeat until the mantissa is
+	  // empty.
+	  for (int nibble_offset = rounded_mantissa_bits - 4;
+	       augmented_mantissa != 0;
+	       augmented_mantissa &= ~(0b1111ull << nibble_offset),
+		 nibble_offset -= 4)
+	    {
+	      const unsigned nibble = augmented_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+	    }
+	  __glibcxx_assert(written_hexits <= effective_precision);
+	  // Since the mantissa is now empty, every hexit hereafter must be '0'.
+	  if (int remaining_hexits = effective_precision - written_hexits)
+	    {
+	      memset(first, '0', remaining_hexits);
+	      first += remaining_hexits;
+	    }
+	}
+
+      // Finally, write the exponent.
+      *first++ = 'p';
+      if (unbiased_exponent >= 0)
+	*first++ = '+';
+      auto result = to_chars(first, last, unbiased_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  static __ryu::floating_decimal_64
+  __floating_to_shortest_scientific(double value)
+  { return __ryu::floating_to_fd64(value); }
+
+  static __ryu::floating_decimal_32
+  __floating_to_shortest_scientific(float value)
+  { return __ryu::floating_to_fd32(value); }
+
+  static int
+  __mantissa_length(__ryu::floating_decimal_32 fd)
+  { return __ryu::decimalLength9(fd.mantissa); }
+
+  static int
+  __mantissa_length(__ryu::floating_decimal_64 fd)
+  { return __ryu::decimalLength17(fd.mantissa); }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_shortest(char* first, char* last, const T value,
+				 chars_format fmt)
+    {
+      if (fmt == chars_format::hex)
+	return __floating_to_chars_hex(first, last, value, nullopt);
+
+      __glibcxx_assert(fmt == chars_format::fixed
+		       || fmt == chars_format::scientific
+		       || fmt == chars_format::general
+		       || fmt == chars_format{});
+      __glibcxx_requires_valid_range(first, last);
+
+      if (auto result = __handle_special_value(first, last, value, fmt, 0))
+	return *result;
+
+      const auto fd = __floating_to_shortest_scientific(value);
+      const int mantissa_length = __mantissa_length(fd);
+      const int scientific_exponent = fd.exponent + mantissa_length - 1;
+
+      if (fmt == chars_format::general)
+	{
+	  // Resolve the 'general' formatting mode as per the specification of
+	  // the 'g' printf output specifier.  Since there is no precision
+	  // argument, the default precision of the 'g' specifier, 6, applies.
+	  if (scientific_exponent >= -4 && scientific_exponent < 6)
+	    fmt = chars_format::fixed;
+	  else
+	    fmt = chars_format::scientific;
+	}
+      else if (fmt == chars_format{})
+	{
+	  // The 'plain' formatting mode resolves to 'scientific' if it yields
+	  // the shorter string, and resolves to 'fixed' otherwise.  The
+	  // following lower and upper bounds on the exponent characterize when
+	  // to prefer 'fixed' over 'scientific'.
+	  int lower_bound = -(mantissa_length + 3);
+	  int upper_bound = 5;
+	  if (mantissa_length == 1)
+	    // The decimal point in scientific notation is omitted in this case;
+	    // tighten the bounds appropriately.
+	    ++lower_bound, --upper_bound;
+
+	  if (fd.exponent >= lower_bound && fd.exponent <= upper_bound)
+	    fmt = chars_format::fixed;
+	  else
+	    fmt = chars_format::scientific;
+	}
+
+      if (fmt == chars_format::scientific)
+	{
+	  // Calculate the total length of the output string, perform a bounds
+	  // check, and then defer to Ryu's to_chars subroutine.
+	  int expected_output_length = fd.sign + mantissa_length;
+	  if (mantissa_length > 1)
+	    expected_output_length += strlen(".");
+	  expected_output_length += ((scientific_exponent >= 100
+				   || scientific_exponent <= -100)
+				  ? strlen("e+NNN") : strlen("e+NN"));
+	  if (last - first < expected_output_length)
+	    return {last, errc::value_too_large};
+
+	  const int output_length = __ryu::to_chars(fd, first);
+	  __glibcxx_assert(output_length == expected_output_length);
+	  return {first + output_length, errc{}};
+	}
+      else if (fmt == chars_format::fixed && fd.exponent >= 0)
+	{
+	  // The Ryu exponent is positive, meaning that this number's shortest
+	  // representation is a whole number, to be formatted in fixed instead
+	  // of scientific notation "as if by std::printf".  This means we may
+	  // need to print more digits of the IEEE mantissa that what the
+	  // shortest scientific form given by Ryu contains.
+	  //
+	  // For instance, the exactly representable number
+	  // 12300000000000001048576.0 has as its shortest scientific
+	  // representation 123e+22, so in this case fd.mantissa is 123 and
+	  // fd.exponent is 22, which doesn't have enough information to format
+	  // the number exactly.  So we defer to Ryu's d2fixed_buffered_n with
+	  // precision=0 to format the number in the general case here.
+
+	  // To that end, first compute the output length and perform a bounds
+	  // check.
+	  int expected_output_length = fd.sign + mantissa_length + fd.exponent;
+	  if (__is_special_pow10_for_fixed_p(fd))
+	    --expected_output_length;
+	  if (last - first < expected_output_length)
+	    return {last, errc::value_too_large};
+
+	  // Optimization: if the shortest representation fits inside the IEEE
+	  // mantissa, then the number is certainly exactly-representable and
+	  // its shortest scientific form must be equal to its exact form.  So
+	  // we can write the value in fixed form exactly via fd.mantissa and
+	  // fd.exponent.
+	  //
+	  // Taking log2 of both sides of the desired condition
+	  //   fd.mantissa * 10^fd.exponent < 2^mantissa_bits
+	  // we get
+	  //   log2 fd.mantissa + fd.exponent * log2 10 < mantissa_bits
+	  // where log2 10 is slightly smaller than 10/3=3.333...
+	  //
+	  // Adding some wiggle room due to rounding yields the condition
+	  // fits_inside_mantissa below.
+	  const int log2_mantissa
+	    = 8*sizeof(long long) - __builtin_clzll(fd.mantissa) - 1;
+	  const bool fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < __floating_type_traits<T>::mantissa_bits - 2);
+	  if (fits_inside_mantissa)
+	    {
+	      // Print the small exactly-represantable number in fixed form by
+	      // writing out fd.mantissa followed by fd.exponent many 0s.
+	      if (fd.sign)
+		*first++ = '-';
+	      to_chars_result result = to_chars(first, last, fd.mantissa);
+	      __glibcxx_assert(result.ec == errc{});
+	      memset(result.ptr, '0', fd.exponent);
+	      result.ptr += fd.exponent;
+	      const int output_length [[maybe_unused]]
+		= fd.sign + (result.ptr - first);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      return result;
+	    }
+	  else
+	    {
+	      // Otherwise, the number is too big, so defer to d2fixed_buffered_n.
+	      const int output_length = __ryu::d2fixed_buffered_n(value, 0,
+								  first);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      return {first + output_length, errc{}};
+	    }
+	}
+      else if (fmt == chars_format::fixed && fd.exponent < 0)
+	{
+	  // The Ryu exponent is negative, so fd.mantissa definitely contains
+	  // all of the whole part of the number, and therefore fd.mantissa and
+	  // fd.exponent contain all of the information needed to format the
+	  // number in fixed notation "as if by std::printf" (with precision
+	  // equal to -fd.exponent).
+	  const int whole_digits = max(mantissa_length + fd.exponent, 1);
+	  const int expected_output_length
+	    = fd.sign + whole_digits + strlen(".") + -fd.exponent;
+	  if (last - first < expected_output_length)
+	    return {last, errc::value_too_large};
+	  if (mantissa_length <= -fd.exponent)
+	    {
+	      // The magnitude of the number is less than one.  Format the
+	      // number appropriately.
+	      const char* orig_first = first;
+	      if (fd.sign)
+		*first++ = '-';
+	      *first++ = '0';
+	      *first++ = '.';
+	      const int leading_zeros = -fd.exponent - mantissa_length;
+	      memset(first, '0', leading_zeros);
+	      first += leading_zeros;
+	      const to_chars_result result = to_chars(first, last, fd.mantissa);
+	      const int output_length [[maybe_unused]] = result.ptr - orig_first;
+	      __glibcxx_assert(output_length == expected_output_length
+			       && result.ec == errc{});
+	      return result;
+	    }
+	  else
+	    {
+	      // The magnitude of the number is at least one.
+	      const char* orig_first = first;
+	      if (fd.sign)
+		*first++ = '-';
+	      to_chars_result result = to_chars(first, last, fd.mantissa);
+	      __glibcxx_assert(result.ec == errc{});
+	      // Make space for and write the decimal point at the appropriate
+	      // spot.
+	      memmove(&result.ptr[fd.exponent+1], &result.ptr[fd.exponent],
+		      -fd.exponent);
+	      result.ptr[fd.exponent] = '.';
+	      const int output_length [[maybe_unused]]
+		= result.ptr + 1 - orig_first;
+	      __glibcxx_assert(output_length == expected_output_length);
+	      ++result.ptr;
+	      return result;
+	    }
+	}
+
+      __builtin_unreachable();
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_precision(char* first, char* last, const T value,
+				  chars_format fmt, int precision)
+    {
+      if (fmt == chars_format::hex)
+	return __floating_to_chars_hex(first, last, value, precision);
+
+      __glibcxx_assert(fmt == chars_format::fixed
+		       || fmt == chars_format::scientific
+		       || fmt == chars_format::general);
+      __glibcxx_requires_valid_range(first, last);
+
+      // A negative precision argument is treated as if it were omitted, in
+      // which case the default precision of 6 applies, as per the printf
+      // specification.
+      if (precision < 0)
+	precision = 6;
+
+      if (auto result = __handle_special_value(first, last, value,
+					       fmt, precision))
+	return *result;
+
+      constexpr int mantissa_bits = __floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = __floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using uint_t = typename __floating_type_traits<T>::uint_t;
+
+      // Extract the sign and exponent from the value.
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(T));
+      const bool sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      const uint32_t biased_exponent
+	= (value_bits >> mantissa_bits) & ((1ull << exponent_bits) - 1u);
+      const bool is_normal_number = (biased_exponent != 0);
+
+      // Calculate the unbiased exponent.
+      const int32_t unbiased_exponent = (is_normal_number
+					 ? biased_exponent - exponent_bias
+					 : 1 - exponent_bias);
+
+      // Obtain trunc(log2(abs(value))), which is just the unbiased exponent.
+      const int floor_log2_value = unbiased_exponent;
+      // This is within +-1 of log10(abs(value)).  Note that log10 2 is 0.3010..
+      const int approx_log10_value = (floor_log2_value >= 0
+				      ? (floor_log2_value*301 + 999)/1000
+				      : (floor_log2_value*301 - 999)/1000);
+
+      // Compute (an upper bound of) the number's effective precision when it is
+      // formatted in scientific and fixed notation.  Beyond this precision all
+      // digits are definitely zero, and this fact allows us to bound the sizes
+      // of any local output buffers that we may need to use.  TODO: consider
+      // the number of trailing zero bits in the mantissa to obtain finer upper
+      // bounds
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits);
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits
+	   : -floor_log2_value + mantissa_bits);
+
+      if (fmt == chars_format::scientific)
+	{
+	  const int effective_precision
+	    = min(precision, max_eff_scientific_precision);
+	  const int excess_precision = precision - effective_precision;
+
+	  // We can easily compute the output length exactly whenever the
+	  // scientific exponent is "far away" from 100.  When the scientific
+	  // exponent is 100, rounding can then change a scientific exponent of
+	  // 99 to 100, or -100 to -99, and our size computation would be off by
+	  // one.  For example, formatting the number 9.96e+99 with precision=1
+	  // gives 1.0e+100, and likewise formatting 9.96e-100 gives 1.0e-99.
+	  // To handle this special case we use an intermediate output buffer to
+	  // write the result.
+	  const bool scientific_exponent_near_100
+	    = ((floor_log2_value >= 332 - 4 && floor_log2_value <= 332 + 4)
+	       || (floor_log2_value >= -333 - 4 && floor_log2_value <= -333 + 4));
+	  int output_length;
+	  if (scientific_exponent_near_100) [[unlikely]]
+	    {
+	      const int output_size_upper_bound
+		= strlen("-d.") + effective_precision + strlen("e+ddd");
+	      char buffer[output_size_upper_bound];
+	      output_length = __ryu::d2exp_buffered_n(value, effective_precision,
+						      buffer, nullptr);
+	      __glibcxx_assert(output_length <= output_size_upper_bound);
+	      if (last - first < output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      memcpy(first, buffer, output_length);
+	    }
+	  else
+	    {
+	      // Compute the output length exactly, perform a bounds check,
+	      // and write the result directly into the range.
+	      const int expected_output_length
+		= (sign + strlen("d") + (effective_precision > 0)
+		   + effective_precision
+		   + ((floor_log2_value >= 332 || floor_log2_value <= -333)
+		      ? strlen("e+ddd") : strlen("e+dd")));
+	      if (last - first < expected_output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      output_length = __ryu::d2exp_buffered_n(value, effective_precision,
+						      first, nullptr);
+	      __glibcxx_assert(output_length <= expected_output_length);
+	    }
+	  first += output_length;
+	  if (excess_precision > 0)
+	    {
+	      // Splice in the excess zeros to the result.
+	      char* const significand_end = (first[-5] == 'e'
+					     ? &first[-5] : &first[-4]);
+	      __glibcxx_assert(*significand_end == 'e');
+	      memmove(significand_end + excess_precision, significand_end,
+		      first - significand_end);
+	      memset(significand_end, '0', excess_precision);
+	      first += excess_precision;
+	    }
+	  return {first, errc{}};
+	}
+      else if (fmt == chars_format::fixed)
+	{
+	  const int effective_precision
+	    = min(precision, max_eff_fixed_precision);
+	  const int excess_precision = precision - effective_precision;
+
+	  // It is harder to compute the exact output length with fixed
+	  // formatting, so we instead compute an upper bound and write to an
+	  // intermediate buffer if necessary.
+	  int output_size_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_size_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_size_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_size_upper_bound += strlen(".") + effective_precision;
+	  int output_length;
+	  if (last - first >= output_size_upper_bound + excess_precision)
+	    {
+	      output_length
+		= __ryu::d2fixed_buffered_n(value, effective_precision, first);
+	      __glibcxx_assert(output_length <= output_size_upper_bound);
+	    }
+	  else
+	    {
+	      char buffer[output_size_upper_bound];
+	      output_length
+		= __ryu::d2fixed_buffered_n(value, effective_precision, buffer);
+	      __glibcxx_assert(output_length <= output_size_upper_bound);
+	      if (last - first < output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      memcpy(first, buffer, output_length);
+	    }
+	  first += output_length;
+	  if (excess_precision > 0)
+	    {
+	      // Append the excess zeros into the result.
+	      memset(first, '0', excess_precision);
+	      first += excess_precision;
+	    }
+	  return {first, errc{}};
+	}
+      else if (fmt == chars_format::general)
+	{
+	  // Handle the 'general' formatting mode as per C11 printf's %g output
+	  // specifier.  Since Ryu doesn't do zero-trimming, we always write to
+	  // an intermediate buffer and manually perform zero-trimming there
+	  // before copying the result over to the output range.
+	  int effective_precision
+	    = min(precision, max_eff_scientific_precision + 1);
+	  const int output_size_upper_bound
+	    = strlen("-d.") + effective_precision + strlen("e+ddd");
+	  // The four bytes of headroom is to avoid needing to do a memmove when
+	  // rewriting a scientific string such as 1.00e-2 into the equivalent
+	  // fixed form 0.001.
+	  char buffer[4 + output_size_upper_bound];
+
+	  // 7.21.6.1/8: "Let P equal ... 1 if the precision is zero."
+	  if (effective_precision == 0)
+	    effective_precision = 1;
+
+	  // Perform a trial formatting in scientific form, and obtain the
+	  // scientific exponent.
+	  int scientific_exponent;
+	  char* buffer_start = buffer + 4;
+	  int output_length
+	    = __ryu::d2exp_buffered_n(value, effective_precision - 1,
+				      buffer_start, &scientific_exponent);
+	  __glibcxx_assert(output_length <= output_size_upper_bound);
+	  // 7.21.6.1/8: "Then, if a conversion with style E would have an
+	  // exponent of X:
+	  //   if P > X >= -4, the conversion is with style f and
+	  //     precision P - (X + 1).
+	  //   otherwise, the conversion is with style e and precision P - 1."
+	  const bool resolve_to_fixed_form
+	    = (scientific_exponent >= -4
+	       && scientific_exponent < effective_precision);
+	  if (resolve_to_fixed_form)
+	    {
+	      // Rather than invoking d2fixed_buffered_n to reformat the number
+	      // for us from scratch, we can just rewrite the scientific form
+	      // into fixed form in-place.  This is safe to do because whenever
+	      // %g resolves to %f, the fixed form will be no larger than the
+	      // corresponding scientific form, and it will also contain the
+	      // same significant digits as the scientific form.
+	      fmt = chars_format::fixed;
+	      if (scientific_exponent < 0)
+		{
+		  // e.g. buffer_start == "-1.234e-04"
+		  char* leading_digit = &buffer_start[sign];
+		  leading_digit[1] = leading_digit[0];
+		  // buffer_start == "-11234e-04"
+		  buffer_start -= -scientific_exponent;
+		  __glibcxx_assert(buffer_start >= buffer);
+		  // buffer_start == "????-11234e-04"
+		  char* head = buffer_start;
+		  if (sign)
+		    *head++ = '-';
+		  *head++ = '0';
+		  *head++ = '.';
+		  memset(head, '0', -scientific_exponent - 1);
+		  // buffer_start == "-0.00011234e-04"
+
+		  // Now drop the exponent suffix, and add the leading zeros to
+		  // the output length.
+		  output_length -= strlen("e-0d");
+		  output_length += -scientific_exponent;
+		  if (effective_precision - 1 == 0)
+		    // The scientific form had no decimal point, but the fixed
+		    // form now does.
+		    output_length += strlen(".");
+		}
+	      else if (effective_precision == 1)
+		{
+		  // The scientific exponent must be 0, so the fixed form
+		  // coincides with the scientific form (minus the exponent
+		  // suffix).
+		  __glibcxx_assert(scientific_exponent == 0);
+		  output_length -= strlen("e+dd");
+		}
+	      else
+		{
+		  // We are dealing with a scientific form which has a
+		  // non-empty fractional part and a nonnegative exponent,
+		  // e.g. buffer_start == "1.234e+02".
+		  __glibcxx_assert(effective_precision >= 1);
+		  char* const decimal_point = &buffer_start[sign + 1];
+		  __glibcxx_assert(*decimal_point == '.');
+		  memmove(decimal_point, decimal_point+1,
+			  scientific_exponent);
+		  // buffer_start == "123.4e+02"
+		  decimal_point[scientific_exponent] = '.';
+		  if (scientific_exponent >= 100)
+		    output_length -= strlen("e+ddd");
+		  else
+		    output_length -= strlen("e+dd");
+		  if (effective_precision - 1 == scientific_exponent)
+		    output_length -= strlen(".");
+		}
+	      effective_precision -= 1 + scientific_exponent;
+
+	      __glibcxx_assert(output_length <= output_size_upper_bound);
+	    }
+	  else
+	    {
+	      // We're sticking to the scientific form, so keep the output as-is.
+	      fmt = chars_format::scientific;
+	      effective_precision = effective_precision - 1;
+	    }
+
+	  // 7.21.6.1/8: "Finally ... any any trailing zeros are removed from
+	  // the fractional portion of the result and the decimal-point
+	  // character is removed if there is no fractional portion remaining."
+	  if (effective_precision > 0)
+	    {
+	      char* decimal_point = nullptr;
+	      if (fmt == chars_format::scientific)
+		decimal_point = &buffer_start[sign + 1];
+	      else if (fmt == chars_format::fixed)
+		decimal_point
+		  = &buffer_start[output_length] - effective_precision - 1;
+	      __glibcxx_assert(*decimal_point == '.');
+
+	      char* const fractional_part_start = decimal_point + 1;
+	      char* fractional_part_end = nullptr;
+	      if (fmt == chars_format::scientific)
+		{
+		  fractional_part_end = (buffer_start[output_length-5] == 'e'
+					 ? &buffer_start[output_length-5]
+					 : &buffer_start[output_length-4]);
+		  __glibcxx_assert(*fractional_part_end == 'e');
+		}
+	      else if (fmt == chars_format::fixed)
+		fractional_part_end = &buffer_start[output_length];
+
+	      const string_view fractional_part
+		= {fractional_part_start, (size_t)(fractional_part_end
+						   - fractional_part_start) };
+	      const size_t last_nonzero_digit_pos
+		= fractional_part.find_last_not_of('0');
+
+	      char* trim_start;
+	      if (last_nonzero_digit_pos == string_view::npos)
+		trim_start = decimal_point;
+	      else
+		trim_start = &fractional_part_start[last_nonzero_digit_pos] + 1;
+	      if (fmt == chars_format::scientific)
+		memmove(trim_start, fractional_part_end,
+			&buffer_start[output_length] - fractional_part_end);
+	      output_length -= fractional_part_end - trim_start;
+	    }
+
+	  if (last - first < output_length)
+	    return {last, errc::value_too_large};
+
+	  memcpy(first, buffer_start, output_length);
+	  return {first + output_length, errc{}};
+	}
+
+      __builtin_unreachable();
+    }
+
+  to_chars_result
+  to_chars(char* first, char* last, double value) noexcept
+  { return __floating_to_chars_shortest(first, last, value, chars_format{}); }
+
+  to_chars_result
+  to_chars(char* first, char* last, float value) noexcept
+  { return __floating_to_chars_shortest(first, last, value, chars_format{}); }
+
+  to_chars_result
+  to_chars(char* first, char* last, double value, chars_format fmt) noexcept
+  { return __floating_to_chars_shortest(first, last, value, fmt); }
+
+  to_chars_result
+  to_chars(char* first, char* last, float value, chars_format fmt) noexcept
+  { return __floating_to_chars_shortest(first, last, value, fmt); }
+
+  to_chars_result
+  to_chars(char* first, char* last, double value, chars_format fmt,
+	   int precision) noexcept
+  { return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+  to_chars_result
+  to_chars(char* first, char* last, float value, chars_format fmt,
+	   int precision) noexcept
+  { return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc
index aedb6561ed4..4b73604cc74 100644
--- a/libstdc++-v3/testsuite/util/testsuite_abi.cc
+++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc
@@ -209,6 +209,7 @@ check_version(symbol& test, bool added)
       known_versions.push_back("GLIBCXX_3.4.26");
       known_versions.push_back("GLIBCXX_3.4.27");
       known_versions.push_back("GLIBCXX_3.4.28");
+      known_versions.push_back("GLIBCXX_3.4.29");
       known_versions.push_back("CXXABI_1.3");
       known_versions.push_back("CXXABI_LDBL_1.3");
       known_versions.push_back("CXXABI_1.3.1");
@@ -240,7 +241,7 @@ check_version(symbol& test, bool added)
 	test.version_status = symbol::incompatible;
 
       // Check that added symbols are added in the latest pre-release version.
-      bool latestp = (test.version_name == "GLIBCXX_3.4.28"
+      bool latestp = (test.version_name == "GLIBCXX_3.4.29"
 		     || test.version_name == "CXXABI_1.3.12"
 		     || test.version_name == "CXXABI_FLOAT128"
 		     || test.version_name == "CXXABI_TM_1");


More information about the Libstdc++-cvs mailing list