public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-12-17 14:43 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-12-17 14:43 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:7015eae2a552876b1ef2d14d75a47c9e69f9751a

commit 7015eae2a552876b1ef2d14d75a47c9e69f9751a
Author: Patrick Palka <ppalka@redhat.com>
Date:   Wed Nov 18 09:21:26 2020 -0500

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms for float, double and long
    double.  We also use Ryu for performing explicit-precision fixed and
    scientific formatting for float and double. For explicit-precision
    formatting for long double we fall back to using printf.  Hexadecimal
    formatting for float, double and long double is implemented from
    scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer only if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the number through
    Ryu a second time when the general formatting mode resolves to fixed
    (which we determine by doing a scientific formatting first and
    inspecting the scientific exponent).  We avoid going through Ryu twice
    by instead transforming the scientific form to the corresponding fixed
    form via in-place string manipulation.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's decision to be consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form for the number 1.08p+0 is 2.1p-1, but we output the former
       instead of the latter, as does printf.
    
    2. The Ryu routine generic_binary_to_decimal that we use for performing
       shortest formatting for large floating point types is implemented
       using the __int128 type, but some targets with a large long double
       type lack __int128 (e.g. i686), so we can't perform shortest
       formatting of long double on such targets through Ryu.  As a
       temporary stopgap this patch makes the long double to_chars overloads
       just dispatch to the double overloads on these targets, which means
       we lose precision in the output.  (We could potentially fix this by
       writing a specialized version of Ryu's generic_binary_to_decimal
       routine that uses uint64_t instead of __int128.)  [Though I wonder if
       there's a better way to work around the lack of __int128 on i686
       specifically?]
    
    3. Our shortest formatting for __ibm128 doesn't guarantee the round-trip
       property if the difference between the high- and low-order exponent
       is large.  This is because we treat __ibm128 as if it has a
       contiguous 105-bit mantissa by merging the mantissas of the high-
       and low-order parts (using code extracted from glibc), so we
       potentially lose precision from the low-order part.  This seems to be
       consistent with how glibc printf formats __ibm128.
    
    libstdc++-v3/ChangeLog:
    
            * config/abi/pre/gnu.ver: Add new exports.
            * include/std/charconv (to_chars): Declare the floating-point
            overloads for float, double and long 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.
            (to_chars): Define for float, double and long double.
            * testsuite/20_util/to_chars/long_double.cc: New test.

Diff:
---
 libstdc++-v3/config/abi/pre/gnu.ver                |    7 +
 libstdc++-v3/include/std/charconv                  |   24 +
 libstdc++-v3/src/c++17/Makefile.am                 |    1 +
 libstdc++-v3/src/c++17/Makefile.in                 |    3 +-
 libstdc++-v3/src/c++17/floating_to_chars.cc        | 1561 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  199 +++
 6 files changed, 1794 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 4b4bd8ab6da..05e0a512247 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2393,6 +2393,13 @@ GLIBCXX_3.4.29 {
     # std::once_flag::_M_finish(bool)
     _ZNSt9once_flag9_M_finishEb;
 
+    # std::to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[defg];
+    # std::to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[defg]St12chars_format;
+    # std::to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[defg]St12chars_formati;
+
 } GLIBCXX_3.4.28;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index dd1ebdf8322..b57b0a16db2 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -702,6 +702,30 @@ namespace __detail
 	     chars_format __fmt = chars_format::general) noexcept;
 #endif
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long 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 37cdb53c076..2ec5ed621ca 100644
--- a/libstdc++-v3/src/c++17/Makefile.am
+++ b/libstdc++-v3/src/c++17/Makefile.am
@@ -51,6 +51,7 @@ endif
 
 sources = \
 	floating_from_chars.cc \
+	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 ccae721ab3f..9b36b7a916c 100644
--- a/libstdc++-v3/src/c++17/Makefile.in
+++ b/libstdc++-v3/src/c++17/Makefile.in
@@ -124,7 +124,7 @@ 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 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
+am__objects_2 = floating_from_chars.lo 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 \
@@ -440,6 +440,7 @@ headers =
 
 sources = \
 	floating_from_chars.cc \
+	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..c2e4a8b0f57
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1561 @@
+// std::to_chars implementation for floating-point types -*- 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 to shake out any bugs.
+#define _GLIBCXX_ASSERTIONS 1
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We support the binary64, float80 (i.e. x86 80-bit extended precision),
+// binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#  define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_BINARY64
+  // When long double is equivalent to double, we just forward the long double
+  // overloads to the double overloads, so we don't need to define a a
+  // floating_type_traits<long double> specialization in this case.
+#elif LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_repr;
+      ieee_repr.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_repr.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_repr.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_repr;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (plus an implicit leading bit).  We use the exponent and sign
+      // of the high part, and we merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_repr;
+      ieee_repr.mantissa = ((uint_t{mantissa_hi} << 64)
+			    | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_repr.biased_exponent = exponent_hi;
+      ieee_repr.sign = sign_hi;
+      return ieee_repr;
+    }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    typename floating_type_traits<T>::shortest_scientific_t
+    floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (std::is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (std::is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (std::is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						mantissa_bits, exponent_bits,
+						!has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  // 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.0 has the shortest scientific form 1e23, so its
+  // exact value is smaller than its shortest scientific form.
+  //
+  // For these powers of 10 the length of the fixed form is one digit less
+  // than what the scientific exponent suggests.
+  //
+  // This subroutine inspects a lookup table to detect when fd is such a
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+				 FP_ZERO, 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())
+      {
+	// We're formatting +-inf or +-nan.
+	if (!__builtin_signbit(value))
+	  str.remove_prefix(strlen("-"));
+
+	if (last - first < (int)str.length())
+	  return {{last, errc::value_too_large}};
+
+	memcpy(first, &str[0], str.length());
+	first += str.length();
+	return {{first, errc{}}};
+      }
+
+    // We're formatting 0.
+    __glibcxx_assert(value == 0);
+    const auto orig_first = first;
+    const bool sign = __builtin_signbit(value);
+    int expected_output_length;
+    switch (fmt)
+      {
+      case chars_format::fixed:
+      case chars_format::scientific:
+      case chars_format::hex:
+	expected_output_length = sign + 1;
+	if (precision)
+	  expected_output_length += strlen(".") + precision;
+	if (fmt == chars_format::scientific)
+	  expected_output_length += strlen("e+00");
+	else if (fmt == chars_format::hex)
+	  expected_output_length += strlen("p+0");
+	if (last - first < expected_output_length)
+	  return {{last, errc::value_too_large}};
+
+	if (sign)
+	  *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;
+	  }
+	break;
+
+      case chars_format::general:
+      default: // case chars_format{}:
+	expected_output_length = sign + 1;
+	if (last - first < expected_output_length)
+	  return {{last, errc::value_too_large}};
+
+	if (sign)
+	  *first++ = '-';
+	*first++ = '0';
+	break;
+      }
+    __glibcxx_assert(first - orig_first == expected_output_length);
+    return {{first, errc{}}};
+  }
+
+// This subroutine of the floating-point to_chars overloads performs
+// hexadecimal formatting.
+template<typename T>
+  static to_chars_result
+  __floating_to_chars_hex(char* first, char* const last, const T value,
+			  const optional<int> precision)
+  {
+    if (precision.has_value() && precision.value() < 0) [[unlikely]]
+      // A negative precision argument is treated as if it were omitted.
+      return __floating_to_chars_hex(first, last, value, nullopt);
+
+    __glibcxx_requires_valid_range(first, last);
+
+    constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+    constexpr bool has_implicit_leading_bit
+      = floating_type_traits<T>::has_implicit_leading_bit;
+    constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+    constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+    using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+    constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+    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.
+    const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+    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;
+    static_assert(mantissa_t_width >= rounded_mantissa_bits);
+    mantissa_t effective_mantissa
+      = ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+    if (is_normal_number)
+      {
+	if constexpr (has_implicit_leading_bit)
+	  // Restore the mantissa's implicit leading bit.
+	  effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	else
+	  // The explicit mantissa bit should already be set.
+	  __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								   - 1u)));
+      }
+
+    // Compute the shortest precision needed to print this value exactly,
+    // disregarding trailing zeros.
+    constexpr int full_hex_precision = (has_implicit_leading_bit
+					? (mantissa_bits + 3) / 4
+					// With an explicit leading bit, we
+					// use the four leading nibbles as the
+					// hexit before the decimal point.
+					: (mantissa_bits - 4 + 3) / 4);
+    const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+    const int shortest_full_precision = full_hex_precision - trailing_zeros;
+    __glibcxx_assert(shortest_full_precision >= 0);
+
+    int written_exponent = unbiased_exponent;
+    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 = mantissa_t;
+	const bitvec round_bit = effective_mantissa << 1;
+	const bitvec has_tail_bits = round_bit - 1;
+	const bitvec lsb_bit = effective_mantissa;
+	const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	// Mask out the dropped nibbles.
+	effective_mantissa >>= dropped_bits;
+	effective_mantissa <<= dropped_bits;
+	if (should_round & (mantissa_t{1} << dropped_bits))
+	  {
+	    // Round up the least significant nibble.
+	    effective_mantissa += mantissa_t{1} << dropped_bits;
+	    // Check and adjust for overflow of the leading nibble.  When the
+	    // type has an implicit leading bit, then the leading nibble
+	    // before rounding is either 0 or 1, so it can't overflow.
+	    if constexpr (!has_implicit_leading_bit)
+	      {
+		// The only supported floating-point type with explicit
+		// leading mantissa bit is LDK_FLOAT80, i.e. x86 80-bit
+		// extended precision, and so we hardcode the below overflow
+		// check+adjustment for this type.
+		static_assert(mantissa_t_width == 64
+			      && rounded_mantissa_bits == 64);
+		if (effective_mantissa == 0)
+		  {
+		    // We rounded up the least significant nibble and the
+		    // mantissa overflowed, e.g f.fcp+10 with precision=1
+		    // became 10.0p+10.  Absorb this extra hexit into the
+		    // exponent to obtain 1.0p+14.
+		    effective_mantissa
+		      = mantissa_t{1} << (rounded_mantissa_bits - 4);
+		    written_exponent += 4;
+		  }
+	      }
+	  }
+      }
+
+    // Compute the leading hexit and mask it out from the mantissa.
+    char leading_hexit;
+    if constexpr (has_implicit_leading_bit)
+      {
+	const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	__glibcxx_assert(nibble <= 2);
+	leading_hexit = '0' + nibble;
+	effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+      }
+    else
+      {
+	const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	__glibcxx_assert(nibble < 16);
+	leading_hexit = "0123456789abcdef"[nibble];
+	effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	written_exponent -= 3;
+      }
+
+    // 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_written_exponent = abs(written_exponent);
+    expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+			       : abs_written_exponent >= 1000 ? strlen("p+dddd")
+			       : abs_written_exponent >= 100 ? strlen("p+ddd")
+			       : abs_written_exponent >= 10 ? strlen("p+dd")
+			       : strlen("p+d"));
+    if (last - first < expected_output_length)
+      return {last, errc::value_too_large};
+
+    const auto saved_first = 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.
+	int nibble_offset = rounded_mantissa_bits;
+	if constexpr (!has_implicit_leading_bit)
+	  // We already printed the entire leading hexit.
+	  nibble_offset -= 4;
+	while (effective_mantissa != 0)
+	  {
+	    nibble_offset -= 4;
+	    const unsigned nibble = effective_mantissa >> nibble_offset;
+	    __glibcxx_assert(nibble < 16);
+	    *first++ = "0123456789abcdef"[nibble];
+	    ++written_hexits;
+	     effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	  }
+	__glibcxx_assert(nibble_offset >= 0);
+	__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 (written_exponent >= 0)
+      *first++ = '+';
+    const to_chars_result result = to_chars(first, last, written_exponent);
+    __glibcxx_assert(result.ec == errc{}
+		     && result.ptr == saved_first + expected_output_length);
+    return result;
+  }
+
+template<typename T>
+  static to_chars_result
+  __floating_to_chars_shortest(char* first, char* const 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 = get_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 will be 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(".");
+	const int abs_exponent = abs(scientific_exponent);
+	expected_output_length += (abs_exponent >= 1000 ? strlen("e+dddd")
+				   : abs_exponent >= 100 ? strlen("e+ddd")
+				   : strlen("e+dd"));
+	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, and so 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 than what the
+	// shortest scientific form given by Ryu provides.
+	//
+	// 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_rounded_up_pow10_p<T>(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...
+	//
+	// After adding some wiggle room due to rounding we get the condition
+	// value_fits_inside_mantissa_p below.
+	const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	const bool value_fits_inside_mantissa_p
+	  = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	     < floating_type_traits<T>::mantissa_bits - 2);
+	if (value_fits_inside_mantissa_p)
+	  {
+	    // 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 = fd.sign + (result.ptr - first);
+	    __glibcxx_assert(output_length == expected_output_length);
+	    return result;
+	  }
+	else if constexpr (is_same_v<T, long double>)
+	  {
+	    // We can't use d2fixed_buffered_n for types larger than double,
+	    // so we instead format larger types through sprintf.
+	    // TODO: We currently go through an intermediate buffer in order
+	    // to accomodate the mandatory null terminator of sprintf, but we
+	    // can avoid this if we use sprintf to write all but the last
+	    // digit, and carefully compute and write the last digit
+	    // ourselves.
+	    char buffer[expected_output_length+1];
+#if _GLIBCXX_USE_C99_FENV_TR1
+	    const int saved_rounding_mode = fegetround();
+	    if (saved_rounding_mode != FE_TONEAREST)
+	      fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+#endif
+	    const int output_length = sprintf(buffer, "%.0Lf", value);
+#if _GLIBCXX_USE_C99_FENV_TR1
+	    if (saved_rounding_mode != FE_TONEAREST)
+	      fesetround(saved_rounding_mode);
+#endif
+	    __glibcxx_assert(output_length == expected_output_length);
+	    memcpy(first, buffer, output_length);
+	    return {first + output_length, errc{}};
+	  }
+	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 auto 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 = 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 auto 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 in the correct spot.
+	    memmove(&result.ptr[fd.exponent+1], &result.ptr[fd.exponent],
+		    -fd.exponent);
+	    result.ptr[fd.exponent] = '.';
+	    const int output_length = result.ptr + 1 - orig_first;
+	    __glibcxx_assert(output_length == expected_output_length);
+	    ++result.ptr;
+	    return result;
+	  }
+      }
+
+    __glibcxx_assert(false);
+  }
+
+template<typename T>
+  static to_chars_result
+  __floating_to_chars_precision(char* first, char* const last, const T value,
+				chars_format fmt, const int precision)
+  {
+    if (fmt == chars_format::hex)
+      return __floating_to_chars_hex(first, last, value, precision);
+
+    if (precision < 0) [[unlikely]]
+      // 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.
+      return __floating_to_chars_precision(first, last, value, fmt, 6);
+
+    __glibcxx_assert(fmt == chars_format::fixed
+		     || fmt == chars_format::scientific
+		     || fmt == chars_format::general);
+    __glibcxx_requires_valid_range(first, last);
+
+    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;
+
+    // Extract the sign and exponent from the value.
+    const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+    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.
+    // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+    // bounds below is necessary only for __ibm128, it seems.  Even though the
+    // type has 105 bits of precision, printf may output 106 fractional digits
+    // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+    const int max_eff_scientific_precision
+      = (floor_log2_value >= 0
+	 ? max(mantissa_bits + 1, approx_log10_value + 1)
+	 : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+    __glibcxx_assert(max_eff_scientific_precision > 0);
+
+    const int max_eff_fixed_precision
+      = (floor_log2_value >= 0
+	 ? mantissa_bits + 1
+	 : -floor_log2_value + mantissa_bits + 1);
+    __glibcxx_assert(max_eff_fixed_precision > 0);
+
+    // Ryu doesn't support formatting floating-point types larger than double
+    // with an explicit precision, so instead we just go through printf.
+    if constexpr (is_same_v<T, long double>)
+      {
+	int effective_precision;
+	const char* output_specifier;
+	if (fmt == chars_format::scientific)
+	  {
+	    effective_precision = min(precision, max_eff_scientific_precision);
+	    output_specifier = "%.*Le";
+	  }
+	else if (fmt == chars_format::fixed)
+	  {
+	    effective_precision = min(precision, max_eff_fixed_precision);
+	    output_specifier = "%.*Lf";
+	  }
+	else if (fmt == chars_format::general)
+	  {
+	    effective_precision = min(precision, max_eff_scientific_precision);
+	    output_specifier = "%.*Lg";
+	  }
+	const int excess_precision = (fmt != chars_format::general
+				      ? precision - effective_precision : 0);
+
+	// Since the output of printf is locale-sensitive, we need to be able
+	// to handle a radix point that's different from '.'.
+	char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	if (effective_precision > 0)
+	  // ???: Can nl_langinfo() ever return null?
+	  if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	    {
+	      strncpy(radix, radix_ptr, sizeof(radix)-1);
+	      // We accept only radix points which are at most 4 bytes (one
+	      // UTF-8 character) wide.
+	      __glibcxx_assert(radix[4] == '\0');
+	    }
+
+	// Compute straightforward upper bounds on the output length.
+	int output_length_upper_bound;
+	if (fmt == chars_format::scientific || fmt == chars_format::general)
+	  output_length_upper_bound = (strlen("-d") + sizeof(radix)
+				       + effective_precision
+				       + strlen("e+dddd"));
+	else if (fmt == chars_format::fixed)
+	  {
+	    if (approx_log10_value >= 0)
+	      output_length_upper_bound = sign + approx_log10_value + 1;
+	    else
+	      output_length_upper_bound = sign + strlen("0");
+	    output_length_upper_bound += sizeof(radix) + effective_precision;
+	  }
+
+	// Do the sprintf into the local buffer.
+	char buffer[output_length_upper_bound+1];
+#if _GLIBCXX_USE_C99_FENV_TR1
+	const int saved_rounding_mode = fegetround();
+	if (saved_rounding_mode != FE_TONEAREST)
+	  fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+#endif
+	int output_length
+	  = sprintf(buffer, output_specifier, effective_precision, value);
+#if _GLIBCXX_USE_C99_FENV_TR1
+	if (saved_rounding_mode != FE_TONEAREST)
+	  fesetround(saved_rounding_mode);
+#endif
+	__glibcxx_assert(output_length <= output_length_upper_bound);
+
+	if (effective_precision > 0)
+	  // We need to replace a radix that is different from '.' with '.'.
+	  if (const string_view radix_sv = {radix}; radix_sv != ".")
+	    {
+	      const string_view buffer_sv = {buffer, (size_t)output_length};
+	      const size_t radix_index = buffer_sv.find(radix_sv);
+	      if (radix_index != string_view::npos)
+		{
+		  buffer[radix_index] = '.';
+		  if (radix_sv.length() > 1)
+		    {
+		      memmove(&buffer[radix_index + 1],
+			      &buffer[radix_index + radix_sv.length()],
+			      output_length - radix_index - radix_sv.length());
+		      output_length -= radix_sv.length() - 1;
+		    }
+		}
+	    }
+
+	// Copy the string from the buffer over to the output range.
+	if (last - first < output_length + excess_precision)
+	  return {last, errc::value_too_large};
+	memcpy(first, buffer, output_length);
+	first += output_length;
+
+	// Add the excess 0s to the result.
+	if (excess_precision > 0)
+	  {
+	    if (fmt == chars_format::scientific)
+	      {
+		char* const significand_end
+		  = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		     : 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;
+	      }
+	    else if (fmt == chars_format::fixed)
+	      {
+		memset(first, '0', excess_precision);
+		first += excess_precision;
+	      }
+	  }
+	return {first, errc{}};
+      }
+    else 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 enough away from +-100.  But if it's
+	// near +-100, then our log2 approximation is too coarse (and doesn't
+	// consider precision-dependent rounding) in order to accurately
+	// distinguish between a scientific exponent of +-100 and +-99.
+	const bool scientific_exponent_near_100_p
+	  = abs(abs(floor_log2_value) - 332) <= 4;
+
+	// Compute an upper bound on the output length.  TODO: Maybe also
+	// consider a lower bound on the output length.
+	int output_length_upper_bound = sign + strlen("d");
+	if (effective_precision > 0)
+	  output_length_upper_bound += strlen(".") + effective_precision;
+	if (scientific_exponent_near_100_p
+	    || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	  output_length_upper_bound += strlen("e+ddd");
+	else
+	  output_length_upper_bound += strlen("e+dd");
+
+	int output_length;
+	if (last - first >= output_length_upper_bound + excess_precision)
+	  {
+	    // The result will definitely fit into the output range, so we can
+	    // write directly into it.
+	    output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						  first, nullptr);
+	    __glibcxx_assert(output_length == output_length_upper_bound
+			     || (scientific_exponent_near_100_p
+				 && (output_length
+				     == output_length_upper_bound - 1)));
+	  }
+	else if (scientific_exponent_near_100_p)
+	  {
+	    // Write the result of d2exp_buffered_n into an intermediate
+	    // buffer, do a bounds check, and copy the result into the output
+	    // range.
+	    char buffer[output_length_upper_bound];
+	    output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						  buffer, nullptr);
+	    __glibcxx_assert(output_length == output_length_upper_bound - 1
+			     || output_length == output_length_upper_bound);
+	    if (last - first < output_length + excess_precision)
+	      return {last, errc::value_too_large};
+	    memcpy(first, buffer, output_length);
+	  }
+	else
+	  // If the scientific exponent is not near 100, then the upper bound
+	  // is actually the exact length, and so the result will definitely
+	  // not fit into the output range.
+	  return {last, errc::value_too_large};
+	first += output_length;
+	if (excess_precision > 0)
+	  {
+	    // Splice the excess zeros into 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;
+
+	// Compute an upper bound on the output length.  TODO: Maybe also
+	// consider a lower bound on the output length.
+	int output_length_upper_bound;
+	if (approx_log10_value >= 0)
+	  output_length_upper_bound = sign + approx_log10_value + 1;
+	else
+	  output_length_upper_bound = sign + strlen("0");
+	if (effective_precision > 0)
+	  output_length_upper_bound += strlen(".") + effective_precision;
+
+	int output_length;
+	if (last - first >= output_length_upper_bound + excess_precision)
+	  {
+	    // The result will definitely fit into the output range, so we can
+	    // write directly into it.
+	    output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						    first);
+	    __glibcxx_assert(output_length <= output_length_upper_bound);
+	  }
+	else
+	  {
+	    // Write the result of d2fixed_buffered_n into an intermediate
+	    // buffer, do a bounds check, and copy the result into the output
+	    // range.
+	    char buffer[output_length_upper_bound];
+	    output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						    buffer);
+	    __glibcxx_assert(output_length <= output_length_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_length_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 form such as 1.00e-2 into the equivalent
+	// fixed form 0.001.
+	char buffer[4 + output_length_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_length_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_length_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{}};
+      }
+
+    __glibcxx_assert(false);
+  }
+
+// Define the overloads for float.
+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, float 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,
+	 int precision) noexcept
+{ return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+// Define the overloads for double.
+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, double 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); }
+
+// Define the overloads for long double.
+to_chars_result
+to_chars(char* first, char* last, long double value) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_shortest(first, last, double(value),
+					chars_format{});
+  else
+    return __floating_to_chars_shortest(first, last, value, chars_format{});
+}
+
+to_chars_result
+to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_shortest(first, last, double(value), fmt);
+  else
+    return __floating_to_chars_shortest(first, last, value, fmt);
+}
+
+to_chars_result
+to_chars(char* first, char* last, long double value, chars_format fmt,
+	 int precision) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_precision(first, last, double(value), fmt,
+					 precision);
+  else
+    return __floating_to_chars_precision(first, last, value, fmt, precision);
+}
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+// Map the -mlong-double-64 long double overloads to the double overloads.
+extern "C" to_chars_result
+_ZSt8to_charsPcS_e(char* first, char* last, double value) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_d")));
+
+extern "C" to_chars_result
+_ZSt8to_charsPcS_eSt12chars_format(char* first, char* last, double value,
+				   chars_format fmt) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_format")));
+
+extern "C" to_chars_result
+_ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
+				    chars_format fmt, int precision) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_formati")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..12ac8ae7822
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,199 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// <charconv> is supported in C++14 as a GNU extension, but this test uses C++17
+// hexadecimal floating-point literals.
+// { dg-do run { target c++17 } }
+// { dg-xfail-run-if "Ryu needs __int128" { large_long_double && { ! int128 } } }
+
+#include <charconv>
+
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Verify that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-11-18 14:53 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-11-18 14:53 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:6c5186678c0e03295db6c7771193680a28dfe820

commit 6c5186678c0e03295db6c7771193680a28dfe820
Author: Patrick Palka <ppalka@redhat.com>
Date:   Wed Nov 18 09:21:26 2020 -0500

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms for float, double and long
    double.  We also use Ryu for performing explicit-precision fixed and
    scientific formatting for float and double. For explicit-precision
    formatting for long double we fall back to using printf.  Hexadecimal
    formatting for float, double and long double is implemented from
    scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer only if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the number through
    Ryu a second time when the general formatting mode resolves to fixed
    (which we determine by doing a scientific formatting first and
    inspecting the scientific exponent).  We avoid going through Ryu twice
    by instead transforming the scientific form to the corresponding fixed
    form via in-place string manipulation.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's decision to be consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form for the number 1.08p+0 is 2.1p-1, but we output the former
       instead of the latter, as does printf.
    
    2. The Ryu routine generic_binary_to_decimal that we use for performing
       shortest formatting for large floating point types is implemented
       using the __int128 type, but some targets with a large long double
       type lack __int128 (e.g. i686), so we can't perform shortest
       formatting of long double on such targets through Ryu.  As a
       temporary stopgap this patch makes the long double to_chars overloads
       just dispatch to the double overloads on these targets, which means
       we lose precision in the output.  (We could potentially fix this by
       writing a specialized version of Ryu's generic_binary_to_decimal
       routine that uses uint64_t instead of __int128.)  [Though I wonder if
       there's a better way to work around the lack of __int128 on i686
       specifically?]
    
    3. Our shortest formatting for __ibm128 doesn't guarantee the round-trip
       property if the difference between the high- and low-order exponent
       is large.  This is because we treat __ibm128 as if it has a
       contiguous 105-bit mantissa by merging the mantissas of the high-
       and low-order parts (using code extracted from glibc), so we
       potentially lose precision from the low-order part.  This seems to be
       consistent with how glibc printf formats __ibm128.
    
    libstdc++-v3/ChangeLog:
    
            * config/abi/pre/gnu.ver: Add new exports.
            * include/std/charconv (to_chars): Declare the floating-point
            overloads for float, double and long 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.
            (to_chars): Define for float, double and long double.
            * testsuite/20_util/to_chars/long_double.cc: New test.

Diff:
---
 libstdc++-v3/config/abi/pre/gnu.ver                |    7 +
 libstdc++-v3/include/std/charconv                  |   24 +
 libstdc++-v3/src/c++17/Makefile.am                 |    1 +
 libstdc++-v3/src/c++17/Makefile.in                 |    3 +-
 libstdc++-v3/src/c++17/floating_to_chars.cc        | 1561 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  199 +++
 6 files changed, 1794 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 46769db1530..05be1d2c7ee 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2393,6 +2393,13 @@ GLIBCXX_3.4.29 {
     # std::once_flag::_M_finish(bool)
     _ZNSt9once_flag9_M_finishEb;
 
+    # std::to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[defg];
+    # std::to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[defg]St12chars_format;
+    # std::to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[defg]St12chars_formati;
+
 } GLIBCXX_3.4.28;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index dd1ebdf8322..b57b0a16db2 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -702,6 +702,30 @@ namespace __detail
 	     chars_format __fmt = chars_format::general) noexcept;
 #endif
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long 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 642efb976ac..0f5b7eb8e56 100644
--- a/libstdc++-v3/src/c++17/Makefile.am
+++ b/libstdc++-v3/src/c++17/Makefile.am
@@ -51,6 +51,7 @@ endif
 
 sources = \
 	floating_from_chars.cc \
+	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 ce08eb3ff11..bf291d7f236 100644
--- a/libstdc++-v3/src/c++17/Makefile.in
+++ b/libstdc++-v3/src/c++17/Makefile.in
@@ -124,7 +124,7 @@ 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 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
+am__objects_2 = floating_from_chars.lo 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 \
@@ -436,6 +436,7 @@ headers =
 
 sources = \
 	floating_from_chars.cc \
+	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..ddc273da4c3
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1561 @@
+// std::to_chars implementation for floating-point types -*- 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 to shake out any bugs.
+#define _GLIBCXX_ASSERTIONS 1
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We support the binary64, float80 (i.e. x86 80-bit extended precision),
+// binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#  define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_BINARY64
+  // When long double is equivalent to double, we just forward the long double
+  // overloads to the double overloads, so we don't need to define a a
+  // floating_type_traits<long double> specialization in this case.
+#elif LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_repr;
+      ieee_repr.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_repr.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_repr.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_repr;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (plus an implicit leading bit).  We use the exponent and sign
+      // of the high part, and we merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_repr;
+      ieee_repr.mantissa = ((uint_t{mantissa_hi} << 64)
+			    | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_repr.biased_exponent = exponent_hi;
+      ieee_repr.sign = sign_hi;
+      return ieee_repr;
+    }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    typename floating_type_traits<T>::shortest_scientific_t
+    floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (std::is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (std::is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (std::is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						mantissa_bits, exponent_bits,
+						!has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  // 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.0 has the shortest scientific form 1e23, so its
+  // exact value is smaller than its shortest scientific form.
+  //
+  // For these powers of 10 the length of the fixed form is one digit less
+  // than what the scientific exponent suggests.
+  //
+  // This subroutine inspects a lookup table to detect when fd is such a
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+				 FP_ZERO, 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())
+      {
+	// We're formatting +-inf or +-nan.
+	if (!__builtin_signbit(value))
+	  str.remove_prefix(strlen("-"));
+
+	if (last - first < (int)str.length())
+	  return {{last, errc::value_too_large}};
+
+	memcpy(first, &str[0], str.length());
+	first += str.length();
+	return {{first, errc{}}};
+      }
+
+    // We're formatting 0.
+    __glibcxx_assert(value == 0);
+    const bool sign = __builtin_signbit(value);
+    int expected_output_length;
+    const char* orig_first = first;
+    switch (fmt)
+      {
+      case chars_format::fixed:
+      case chars_format::scientific:
+      case chars_format::hex:
+	expected_output_length = sign + 1;
+	if (precision)
+	  expected_output_length += strlen(".") + precision;
+	if (fmt == chars_format::scientific)
+	  expected_output_length += strlen("e+00");
+	else if (fmt == chars_format::hex)
+	  expected_output_length += strlen("p+0");
+	if (last - first < expected_output_length)
+	  return {{last, errc::value_too_large}};
+
+	if (sign)
+	  *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;
+	  }
+	break;
+
+      case chars_format::general:
+      default: // case chars_format{}:
+	expected_output_length = sign + 1;
+	if (last - first < expected_output_length)
+	  return {{last, errc::value_too_large}};
+
+	if (sign)
+	  *first++ = '-';
+	*first++ = '0';
+	break;
+      }
+    __glibcxx_assert(first - orig_first == expected_output_length);
+    return {{first, errc{}}};
+  }
+
+// This subroutine of the floating-point to_chars overloads performs
+// hexadecimal formatting.
+template<typename T>
+  static to_chars_result
+  __floating_to_chars_hex(char* first, char* const last, const T value,
+			  const optional<int> precision)
+  {
+    if (precision.has_value() && precision.value() < 0) [[unlikely]]
+      // A negative precision argument is treated as if it were omitted.
+      return __floating_to_chars_hex(first, last, value, nullopt);
+
+    __glibcxx_requires_valid_range(first, last);
+
+    constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+    constexpr bool has_implicit_leading_bit
+      = floating_type_traits<T>::has_implicit_leading_bit;
+    constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+    constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+    using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+    constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+    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.
+    const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+    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;
+    static_assert(mantissa_t_width >= rounded_mantissa_bits);
+    mantissa_t effective_mantissa
+      = ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+    if (is_normal_number)
+      {
+	if constexpr (has_implicit_leading_bit)
+	  // Restore the mantissa's implicit leading bit.
+	  effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	else
+	  // The explicit mantissa bit should already be set.
+	  __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								   - 1u)));
+      }
+
+    // Compute the shortest precision needed to print this value exactly,
+    // disregarding trailing zeros.
+    constexpr int full_hex_precision = (has_implicit_leading_bit
+					? (mantissa_bits + 3) / 4
+					// With an explicit leading bit, we
+					// use the four leading nibbles as the
+					// hexit before the decimal point.
+					: (mantissa_bits - 4 + 3) / 4);
+    const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+    const int shortest_full_precision = full_hex_precision - trailing_zeros;
+    __glibcxx_assert(shortest_full_precision >= 0);
+
+    int written_exponent = unbiased_exponent;
+    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 = mantissa_t;
+	const bitvec round_bit = effective_mantissa << 1;
+	const bitvec has_tail_bits = round_bit - 1;
+	const bitvec lsb_bit = effective_mantissa;
+	const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	// Mask out the dropped nibbles.
+	effective_mantissa >>= dropped_bits;
+	effective_mantissa <<= dropped_bits;
+	if (should_round & (mantissa_t{1} << dropped_bits))
+	  {
+	    // Round up the least significant nibble.
+	    effective_mantissa += mantissa_t{1} << dropped_bits;
+	    // Check and adjust for overflow of the leading nibble.  When the
+	    // type has an implicit leading bit, then the leading nibble
+	    // before rounding is either 0 or 1, so it can't overflow.
+	    if constexpr (!has_implicit_leading_bit)
+	      {
+		// The only supported floating-point type with explicit
+		// leading mantissa bit is LDK_FLOAT80, i.e. x86 80-bit
+		// extended precision, and so we hardcode the below overflow
+		// check+adjustment for this type.
+		static_assert(mantissa_t_width == 64
+			      && rounded_mantissa_bits == 64);
+		if (effective_mantissa == 0)
+		  {
+		    // We rounded up the least significant nibble and the
+		    // mantissa overflowed, e.g f.fcp+10 with precision=1
+		    // became 10.0p+10.  Absorb this extra hexit into the
+		    // exponent to obtain 1.0p+14.
+		    effective_mantissa
+		      = mantissa_t{1} << (rounded_mantissa_bits - 4);
+		    written_exponent += 4;
+		  }
+	      }
+	  }
+      }
+
+    // Compute the leading hexit and mask it out from the mantissa.
+    char leading_hexit;
+    if constexpr (has_implicit_leading_bit)
+      {
+	const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	__glibcxx_assert(nibble <= 2);
+	leading_hexit = '0' + nibble;
+	effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+      }
+    else
+      {
+	const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	__glibcxx_assert(nibble < 16);
+	leading_hexit = "0123456789abcdef"[nibble];
+	effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	written_exponent -= 3;
+      }
+
+    // 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_written_exponent = abs(written_exponent);
+    expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+			       : abs_written_exponent >= 1000 ? strlen("p+dddd")
+			       : abs_written_exponent >= 100 ? strlen("p+ddd")
+			       : abs_written_exponent >= 10 ? strlen("p+dd")
+			       : strlen("p+d"));
+    if (last - first < expected_output_length)
+      return {last, errc::value_too_large};
+
+    const auto saved_first = 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.
+	int nibble_offset = rounded_mantissa_bits;
+	if constexpr (!has_implicit_leading_bit)
+	  // We already printed the entire leading hexit.
+	  nibble_offset -= 4;
+	while (effective_mantissa != 0)
+	  {
+	    nibble_offset -= 4;
+	    const unsigned nibble = effective_mantissa >> nibble_offset;
+	    __glibcxx_assert(nibble < 16);
+	    *first++ = "0123456789abcdef"[nibble];
+	    ++written_hexits;
+	     effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	  }
+	__glibcxx_assert(nibble_offset >= 0);
+	__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 (written_exponent >= 0)
+      *first++ = '+';
+    const to_chars_result result = to_chars(first, last, written_exponent);
+    __glibcxx_assert(result.ec == errc{}
+		     && result.ptr == saved_first + expected_output_length);
+    return result;
+  }
+
+template<typename T>
+  static to_chars_result
+  __floating_to_chars_shortest(char* first, char* const 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 = get_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 will be 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(".");
+	const int abs_exponent = abs(scientific_exponent);
+	expected_output_length += (abs_exponent >= 1000 ? strlen("e+dddd")
+				   : abs_exponent >= 100 ? strlen("e+ddd")
+				   : strlen("e+dd"));
+	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, and so 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 than what the
+	// shortest scientific form given by Ryu provides.
+	//
+	// 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_rounded_up_pow10_p<T>(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...
+	//
+	// After adding some wiggle room due to rounding we get the condition
+	// value_fits_inside_mantissa_p below.
+	const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	const bool value_fits_inside_mantissa_p
+	  = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	     < floating_type_traits<T>::mantissa_bits - 2);
+	if (value_fits_inside_mantissa_p)
+	  {
+	    // 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 = fd.sign + (result.ptr - first);
+	    __glibcxx_assert(output_length == expected_output_length);
+	    return result;
+	  }
+	else if constexpr (is_same_v<T, long double>)
+	  {
+	    // We can't use d2fixed_buffered_n for types larger than double,
+	    // so we instead format larger types through sprintf.
+	    // TODO: We currently go through an intermediate buffer in order
+	    // to accomodate the mandatory null terminator of sprintf, but we
+	    // can avoid this if we use sprintf to write all but the last
+	    // digit, and carefully compute and write the last digit
+	    // ourselves.
+	    char buffer[expected_output_length+1];
+#if _GLIBCXX_USE_C99_FENV_TR1
+	    const int saved_rounding_mode = fegetround();
+	    if (saved_rounding_mode != FE_TONEAREST)
+	      fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+#endif
+	    const int output_length = sprintf(buffer, "%.0Lf", value);
+#if _GLIBCXX_USE_C99_FENV_TR1
+	    if (saved_rounding_mode != FE_TONEAREST)
+	      fesetround(saved_rounding_mode);
+#endif
+	    __glibcxx_assert(output_length == expected_output_length);
+	    memcpy(first, buffer, output_length);
+	    return {first + output_length, errc{}};
+	  }
+	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 = 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 in the correct spot.
+	    memmove(&result.ptr[fd.exponent+1], &result.ptr[fd.exponent],
+		    -fd.exponent);
+	    result.ptr[fd.exponent] = '.';
+	    const int output_length = result.ptr + 1 - orig_first;
+	    __glibcxx_assert(output_length == expected_output_length);
+	    ++result.ptr;
+	    return result;
+	  }
+      }
+
+    __glibcxx_assert(false);
+  }
+
+template<typename T>
+  static to_chars_result
+  __floating_to_chars_precision(char* first, char* const last, const T value,
+				chars_format fmt, const int precision)
+  {
+    if (fmt == chars_format::hex)
+      return __floating_to_chars_hex(first, last, value, precision);
+
+    if (precision < 0) [[unlikely]]
+      // 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.
+      return __floating_to_chars_precision(first, last, value, fmt, 6);
+
+    __glibcxx_assert(fmt == chars_format::fixed
+		     || fmt == chars_format::scientific
+		     || fmt == chars_format::general);
+    __glibcxx_requires_valid_range(first, last);
+
+    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;
+
+    // Extract the sign and exponent from the value.
+    const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+    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.
+    // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+    // bounds below is necessary only for __ibm128, it seems.  Even though the
+    // type has 105 bits of precision, printf may output 106 fractional digits
+    // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+    const int max_eff_scientific_precision
+      = (floor_log2_value >= 0
+	 ? max(mantissa_bits + 1, approx_log10_value + 1)
+	 : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+    __glibcxx_assert(max_eff_scientific_precision > 0);
+
+    const int max_eff_fixed_precision
+      = (floor_log2_value >= 0
+	 ? mantissa_bits + 1
+	 : -floor_log2_value + mantissa_bits + 1);
+    __glibcxx_assert(max_eff_fixed_precision > 0);
+
+    // Ryu doesn't support formatting floating-point types larger than double
+    // with an explicit precision, so instead we just go through printf.
+    if constexpr (is_same_v<T, long double>)
+      {
+	int effective_precision;
+	const char* output_specifier;
+	if (fmt == chars_format::scientific)
+	  {
+	    effective_precision = min(precision, max_eff_scientific_precision);
+	    output_specifier = "%.*Le";
+	  }
+	else if (fmt == chars_format::fixed)
+	  {
+	    effective_precision = min(precision, max_eff_fixed_precision);
+	    output_specifier = "%.*Lf";
+	  }
+	else if (fmt == chars_format::general)
+	  {
+	    effective_precision = min(precision, max_eff_scientific_precision);
+	    output_specifier = "%.*Lg";
+	  }
+	const int excess_precision = (fmt != chars_format::general
+				      ? precision - effective_precision : 0);
+
+	// Since the output of printf is locale-sensitive, we need to be able
+	// to handle a radix point that's different from '.'.
+	char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	if (effective_precision > 0)
+	  // ???: Can nl_langinfo() ever return null?
+	  if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	    {
+	      strncpy(radix, radix_ptr, sizeof(radix)-1);
+	      // We accept only radix points which are at most 4 bytes (one
+	      // UTF-8 character) wide.
+	      __glibcxx_assert(radix[4] == '\0');
+	    }
+
+	// Compute straightforward upper bounds on the output length.
+	int output_length_upper_bound;
+	if (fmt == chars_format::scientific || fmt == chars_format::general)
+	  output_length_upper_bound = (strlen("-d") + sizeof(radix)
+				       + effective_precision
+				       + strlen("e+dddd"));
+	else if (fmt == chars_format::fixed)
+	  {
+	    if (approx_log10_value >= 0)
+	      output_length_upper_bound = sign + approx_log10_value + 1;
+	    else
+	      output_length_upper_bound = sign + strlen("0");
+	    output_length_upper_bound += sizeof(radix) + effective_precision;
+	  }
+
+	// Do the sprintf into the local buffer.
+	char buffer[output_length_upper_bound+1];
+#if _GLIBCXX_USE_C99_FENV_TR1
+	const int saved_rounding_mode = fegetround();
+	if (saved_rounding_mode != FE_TONEAREST)
+	  fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+#endif
+	int output_length
+	  = sprintf(buffer, output_specifier, effective_precision, value);
+#if _GLIBCXX_USE_C99_FENV_TR1
+	if (saved_rounding_mode != FE_TONEAREST)
+	  fesetround(saved_rounding_mode);
+#endif
+	__glibcxx_assert(output_length <= output_length_upper_bound);
+
+	if (effective_precision > 0)
+	  // We need to replace a radix that is different from '.' with '.'.
+	  if (const string_view radix_sv = {radix}; radix_sv != ".")
+	    {
+	      const string_view buffer_sv = {buffer, (size_t)output_length};
+	      const size_t radix_index = buffer_sv.find(radix_sv);
+	      if (radix_index != string_view::npos)
+		{
+		  buffer[radix_index] = '.';
+		  if (radix_sv.length() > 1)
+		    {
+		      memmove(&buffer[radix_index + 1],
+			      &buffer[radix_index + radix_sv.length()],
+			      output_length - radix_index - radix_sv.length());
+		      output_length -= radix_sv.length() - 1;
+		    }
+		}
+	    }
+
+	// Copy the string from the buffer over to the output range.
+	if (last - first < output_length + excess_precision)
+	  return {last, errc::value_too_large};
+	memcpy(first, buffer, output_length);
+	first += output_length;
+
+	// Add the excess 0s to the result.
+	if (excess_precision > 0)
+	  {
+	    if (fmt == chars_format::scientific)
+	      {
+		char* const significand_end
+		  = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		     : 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;
+	      }
+	    else if (fmt == chars_format::fixed)
+	      {
+		memset(first, '0', excess_precision);
+		first += excess_precision;
+	      }
+	  }
+	return {first, errc{}};
+      }
+    else 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 enough away from +-100.  But if it's
+	// near +-100, then our log2 approximation is too coarse (and doesn't
+	// consider precision-dependent rounding) in order to accurately
+	// distinguish between a scientific exponent of +-100 and +-99.
+	const bool scientific_exponent_near_100_p
+	  = abs(abs(floor_log2_value) - 332) <= 4;
+
+	// Compute an upper bound on the output length.  TODO: Maybe also
+	// consider a lower bound on the output length.
+	int output_length_upper_bound = sign + strlen("d");
+	if (effective_precision > 0)
+	  output_length_upper_bound += strlen(".") + effective_precision;
+	if (scientific_exponent_near_100_p
+	    || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	  output_length_upper_bound += strlen("e+ddd");
+	else
+	  output_length_upper_bound += strlen("e+dd");
+
+	int output_length;
+	if (last - first >= output_length_upper_bound + excess_precision)
+	  {
+	    // The result will definitely fit into the output range, so we can
+	    // write directly into it.
+	    output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						  first, nullptr);
+	    __glibcxx_assert(output_length == output_length_upper_bound
+			     || (scientific_exponent_near_100_p
+				 && (output_length
+				     == output_length_upper_bound - 1)));
+	  }
+	else if (scientific_exponent_near_100_p)
+	  {
+	    // Write the result of d2exp_buffered_n into an intermediate
+	    // buffer, do a bounds check, and copy the result into the output
+	    // range.
+	    char buffer[output_length_upper_bound];
+	    output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						  buffer, nullptr);
+	    __glibcxx_assert(output_length == output_length_upper_bound - 1
+			     || output_length == output_length_upper_bound);
+	    if (last - first < output_length + excess_precision)
+	      return {last, errc::value_too_large};
+	    memcpy(first, buffer, output_length);
+	  }
+	else
+	  // If the scientific exponent is not near 100, then the upper bound
+	  // is actually the exact length, and so the result will definitely
+	  // not fit into the output range.
+	  return {last, errc::value_too_large};
+	first += output_length;
+	if (excess_precision > 0)
+	  {
+	    // Splice the excess zeros into 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;
+
+	// Compute an upper bound on the output length.  TODO: Maybe also
+	// consider a lower bound on the output length.
+	int output_length_upper_bound;
+	if (approx_log10_value >= 0)
+	  output_length_upper_bound = sign + approx_log10_value + 1;
+	else
+	  output_length_upper_bound = sign + strlen("0");
+	if (effective_precision > 0)
+	  output_length_upper_bound += strlen(".") + effective_precision;
+
+	int output_length;
+	if (last - first >= output_length_upper_bound + excess_precision)
+	  {
+	    // The result will definitely fit into the output range, so we can
+	    // write directly into it.
+	    output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						    first);
+	    __glibcxx_assert(output_length <= output_length_upper_bound);
+	  }
+	else
+	  {
+	    // Write the result of d2fixed_buffered_n into an intermediate
+	    // buffer, do a bounds check, and copy the result into the output
+	    // range.
+	    char buffer[output_length_upper_bound];
+	    output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						    buffer);
+	    __glibcxx_assert(output_length <= output_length_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_length_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 form such as 1.00e-2 into the equivalent
+	// fixed form 0.001.
+	char buffer[4 + output_length_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_length_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_length_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{}};
+      }
+
+    __glibcxx_assert(false);
+  }
+
+// Define the overloads for float.
+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, float 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,
+	 int precision) noexcept
+{ return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+// Define the overloads for double.
+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, double 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); }
+
+// Define the overloads for long double.
+to_chars_result
+to_chars(char* first, char* last, long double value) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_shortest(first, last, double(value),
+					chars_format{});
+  else
+    return __floating_to_chars_shortest(first, last, value, chars_format{});
+}
+
+to_chars_result
+to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_shortest(first, last, double(value), fmt);
+  else
+    return __floating_to_chars_shortest(first, last, value, fmt);
+}
+
+to_chars_result
+to_chars(char* first, char* last, long double value, chars_format fmt,
+	 int precision) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_precision(first, last, double(value), fmt,
+					 precision);
+  else
+    return __floating_to_chars_precision(first, last, value, fmt, precision);
+}
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+// Map the -mlong-double-64 long double overloads to the double overloads.
+extern "C" to_chars_result
+_ZSt8to_charsPcS_e(char* first, char* last, double value) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_d")));
+
+extern "C" to_chars_result
+_ZSt8to_charsPcS_eSt12chars_format(char* first, char* last, double value,
+				   chars_format fmt) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_format")));
+
+extern "C" to_chars_result
+_ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
+				    chars_format fmt, int precision) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_formati")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..12ac8ae7822
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,199 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// <charconv> is supported in C++14 as a GNU extension, but this test uses C++17
+// hexadecimal floating-point literals.
+// { dg-do run { target c++17 } }
+// { dg-xfail-run-if "Ryu needs __int128" { large_long_double && { ! int128 } } }
+
+#include <charconv>
+
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Verify that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-11-18 14:22 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-11-18 14:22 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:2fddc758609cbffc0b1ba40b9b1a597cb0e094ac

commit 2fddc758609cbffc0b1ba40b9b1a597cb0e094ac
Author: Patrick Palka <ppalka@redhat.com>
Date:   Wed Nov 18 09:21:26 2020 -0500

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms for float, double and long
    double.  We also use Ryu for performing explicit-precision fixed and
    scientific formatting for float and double. For explicit-precision
    formatting for long double we fall back to using printf.  Hexadecimal
    formatting for float, double and long double is implemented from
    scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer only if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the number through
    Ryu a second time when the general formatting mode resolves to fixed
    (which we determine by doing a scientific formatting first and
    inspecting the scientific exponent).  We avoid going through Ryu twice
    by instead transforming the scientific form to the corresponding fixed
    form via in-place string manipulation.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's decision to be consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form for the number 1.08p+0 is 2.1p-1, but we output the former
       instead of the latter, as does printf.
    
    2. The Ryu routine generic_binary_to_decimal that we use for performing
       shortest formatting for large floating point types is implemented
       using the __int128 type, but some targets with a large long double
       type lack __int128 (e.g. i686), so we can't perform shortest
       formatting of long double on such targets through Ryu.  As a
       temporary stopgap this patch makes the long double to_chars overloads
       just dispatch to the double overloads on these targets, which means
       we lose precision in the output.  (We could potentially fix this by
       writing a specialized version of Ryu's generic_binary_to_decimal
       routine that uses uint64_t instead of __int128.)  [Though I wonder if
       there's a better way to work around the lack of __int128 on i686
       specifically?]
    
    3. Our shortest formatting for __ibm128 doesn't guarantee the round-trip
       property if the difference between the high- and low-order exponent
       is large.  This is because we treat __ibm128 as if it has a
       contiguous 105-bit mantissa by merging the mantissas of the high-
       and low-order parts (using code extracted from glibc), so we
       potentially lose precision from the low-order part.  This seems to be
       consistent with how glibc printf formats __ibm128.
    
    libstdc++-v3/ChangeLog:
    
            * config/abi/pre/gnu.ver: Add new exports.
            * include/std/charconv (to_chars): Declare the floating-point
            overloads for float, double and long 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.
            (to_chars): Define for float, double and long double.
            * testsuite/20_util/to_chars/long_double.cc: New test.

Diff:
---
 libstdc++-v3/config/abi/pre/gnu.ver                |    7 +
 libstdc++-v3/include/std/charconv                  |   24 +
 libstdc++-v3/src/c++17/Makefile.am                 |    1 +
 libstdc++-v3/src/c++17/Makefile.in                 |    3 +-
 libstdc++-v3/src/c++17/floating_to_chars.cc        | 1564 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  199 +++
 6 files changed, 1797 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 46769db1530..05be1d2c7ee 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2393,6 +2393,13 @@ GLIBCXX_3.4.29 {
     # std::once_flag::_M_finish(bool)
     _ZNSt9once_flag9_M_finishEb;
 
+    # std::to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[defg];
+    # std::to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[defg]St12chars_format;
+    # std::to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[defg]St12chars_formati;
+
 } GLIBCXX_3.4.28;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index dd1ebdf8322..b57b0a16db2 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -702,6 +702,30 @@ namespace __detail
 	     chars_format __fmt = chars_format::general) noexcept;
 #endif
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long 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 642efb976ac..0f5b7eb8e56 100644
--- a/libstdc++-v3/src/c++17/Makefile.am
+++ b/libstdc++-v3/src/c++17/Makefile.am
@@ -51,6 +51,7 @@ endif
 
 sources = \
 	floating_from_chars.cc \
+	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 ce08eb3ff11..bf291d7f236 100644
--- a/libstdc++-v3/src/c++17/Makefile.in
+++ b/libstdc++-v3/src/c++17/Makefile.in
@@ -124,7 +124,7 @@ 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 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
+am__objects_2 = floating_from_chars.lo 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 \
@@ -436,6 +436,7 @@ headers =
 
 sources = \
 	floating_from_chars.cc \
+	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..cedb3557128
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1564 @@
+// std::to_chars implementation for floating-point types -*- 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 to shake out any bugs.
+#define _GLIBCXX_ASSERTIONS
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We support the binary64, float80 (i.e. x86 80-bit extended precision),
+// binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#  define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+  struct floating_type_traits<float>
+  {
+    // We (and Ryu) assume float has the IEEE binary32 format.
+    static_assert(__FLT_MANT_DIG__ == 24);
+    static constexpr int mantissa_bits = 23;
+    static constexpr int exponent_bits = 8;
+    static constexpr bool has_implicit_leading_bit = true;
+    using mantissa_t = uint32_t;
+    using shortest_scientific_t = ryu::floating_decimal_32;
+
+    static constexpr uint64_t pow10_adjustment_tab[]
+      = { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+  };
+
+  template<>
+  struct floating_type_traits<double>
+  {
+    // We (and Ryu) assume double has the IEEE binary64 format.
+    static_assert(__DBL_MANT_DIG__ == 53);
+    static constexpr int mantissa_bits = 52;
+    static constexpr int exponent_bits = 11;
+    static constexpr bool has_implicit_leading_bit = true;
+    using mantissa_t = uint64_t;
+    using shortest_scientific_t = ryu::floating_decimal_64;
+
+    static constexpr uint64_t pow10_adjustment_tab[]
+      = { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	  0b0111100011110101011000011110000000110110010101011000001110011111,
+	  0b0101101100000000011100100100111100110110110100010001010101110000,
+	  0b0011110010111000101111110101100011101100010001010000000101100111,
+	  0b0001010000011001011100100001010000010101101000001101000000000000 };
+  };
+
+#if LONG_DOUBLE_KIND == LDK_BINARY64
+  // When long double is equivalent to double, we just forward the long double
+  // overloads to the double overloads, so we don't need to define a a
+  // floating_type_traits<long double> specialization in this case.
+#elif LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+  struct floating_type_traits<long double>
+  {
+    static constexpr int mantissa_bits = 64;
+    static constexpr int exponent_bits = 15;
+    static constexpr bool has_implicit_leading_bit = false;
+    using mantissa_t = uint64_t;
+    using shortest_scientific_t = ryu::floating_decimal_128;
+
+    static constexpr uint64_t pow10_adjustment_tab[]
+      = { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	  0b1001100101001111010011011111101000101111110001011001011101110000,
+	  0b0000101111111011110010001000001010111101011110111111010100011001,
+	  0b0011100000011111001101101011111001111100100010000101001111101001,
+	  0b0100100100000000100111010010101110011000110001101101110011001010,
+	  0b0111100111100010100000010011000010010110101111110101000011110100,
+	  0b1010100111100010011110000011011101101100010110000110101010101010,
+	  0b0000001111001111000000101100111011011000101000110011101100110010,
+	  0b0111000011100100101101010100001101111110101111001000010011111111,
+	  0b0010111000100110100100100010101100111010110001101010010111001000,
+	  0b0000100000010110000011001001000111000001111010100101101000001111,
+	  0b0010101011101000111100001011000010011101000101010010010000101111,
+	  0b1011111011101101110010101011010001111000101000101101011001100011,
+	  0b1010111011011011110111110011001010000010011001110100101101000101,
+	  0b0011000001110110011010010000011100100011001011001100001101010110,
+	  0b0100011111011000111111101000011110000010111110101001000000001001,
+	  0b1110000001110001001101101110011000100000001010000111100010111010,
+	  0b1110001001010011101000111000001000010100110000010110100011110000,
+	  0b0000011010110000110001111000011111000011001101001101001001000110,
+	  0b1010010111001000101001100101010110100100100010010010000101000010,
+	  0b1011001110000111100010100110000011100011111001110111001100000101,
+	  0b0110101001001000010110001000010001010101110101100001111100011001,
+	  0b1111100011110101011110011010101001010010100011000010110001101001,
+	  0b0100000100001000111101011100010011011111011001000000001100011000,
+	  0b1110111111000111100101110111110000000011001110011100011011011001,
+	  0b1100001100100000010001100011011000111011110000110011010101000011,
+	  0b1111111011100111011101001111111000010000001111010111110010000100,
+	  0b1110111001111110101111000101000000001010001110011010001000111010,
+	  0b1000010001011000101111111010110011111101110101101001111000111010,
+	  0b0100000111101001000111011001101000001010111011101001101111000100,
+	  0b0000011100110001000111011100111100110001101111111010110111100000,
+	  0b0000011101011100100110010011110101010100010011110010010111010000,
+	  0b0011011001100111110101111100001001101110101101001110110011110110,
+	  0b1011000101000001110100111001100100111100110011110000000001101000,
+	  0b1011100011110100001001110101010110111001000000001011101001011110,
+	  0b1111001010010010100000010110101010101011101000101000000000001100,
+	  0b1000001111100100111001110101100001010011111111000001000011110000,
+	  0b0001011101001000010000101101111000001110101100110011001100110111,
+	  0b1110011100000010101011011111001010111101111110100000011100000011,
+	  0b1001110110011100101010011110100010110001001110110000101011100110,
+	  0b1001101000100011100111010000011011100001000000110101100100001001,
+	  0b1010111000101000101101010111000010001100001010100011111100000100,
+	  0b0111101000100011000101101011111011100010001101110111001111001011,
+	  0b1110100111010110001110110110000000010110100011110000010001111100,
+	  0b1100010100011010001011001000111001010101011110100101011001000000,
+	  0b0000110001111001100110010110111010101101001101000000000010010101,
+	  0b0001110111101000001111101010110010010000111110111100000111110100,
+	  0b0111110111001001111000110001101101001010101110110101111110000100,
+	  0b0000111110111010101111100010111010011100010110011011011001000001,
+	  0b1010010100100100101110111111111000101100000010111111101101000110,
+	  0b1000100111111101100011001101000110001000000100010101010100001101,
+	  0b1100101010101000111100101100001000110001110010100000000010110101,
+	  0b1010000100111101100100101010010110100010000000110101101110000100,
+	  0b1011111011110001110000100100000000001010111010001101100000100100,
+	  0b0111101101100011001110011100000001000101101101111000100111011111,
+	  0b0100111010010011011001010011110100001100111010010101111111100011,
+	  0b0010001001011000111000001100110111110111110010100011000110110110,
+	  0b0101010110000000010000100000110100111011111101000100000111010010,
+	  0b0110000011011101000001010100110101101110011100110101000000001001,
+	  0b1101100110100000011000001111000100100100110001100110101010101100,
+	  0b0010100101010110010010001010101000011111111111001011001010001111,
+	  0b0111001010001111001100111001010101001000110101000011110000001000,
+	  0b0110010011001001001111110001010010001011010010001101110110110011,
+	  0b0110010100111011000100111000001001101011111001110010111110111111,
+	  0b0101110111001001101100110100101001110010101110011001101110001000,
+	  0b0100110101010111011010001100010111100011010011111001010100111000,
+	  0b0111000110110111011110100100010111000110000110110110110001111110,
+	  0b1000101101010100100100111110100011110110110010011001110011110101,
+	  0b1001101110101001010100111101101011000101000010110101101111110000,
+	  0b0100100101001011011001001011000010001101001010010001010110101000,
+	  0b0010100001001011100110101000010110000111000111000011100101011011,
+	  0b0110111000011001111101101011111010001000000010101000101010011110,
+	  0b1000110110100001111011000001111100001001000000010110010100100100,
+	  0b1001110100011111100111101011010000010101011100101000010010100110,
+	  0b0001010110101110100010101010001110110110100011101010001001111100,
+	  0b1010100101101100000010110011100110100010010000100100001110000100,
+	  0b0001000000010000001010000010100110000001110100111001110111101101,
+	  0b1100000000000000000000000000000000000000000000000000000000000000 };
+  };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+  struct floating_type_traits<long double>
+  {
+    static constexpr int mantissa_bits = 112;
+    static constexpr int exponent_bits = 15;
+    static constexpr bool has_implicit_leading_bit = true;
+    using mantissa_t = unsigned __int128;
+    using shortest_scientific_t = ryu::floating_decimal_128;
+
+    static constexpr uint64_t pow10_adjustment_tab[]
+      = { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	  0b1011001111110100000100010101101110011100100110000110010110011000,
+	  0b1010100010001101111111000000001101010010100010010000111011110111,
+	  0b1011111001110001111000011111000010110111000111110100101010100101,
+	  0b0110100110011110011011000011000010011001110001001001010011100011,
+	  0b0000011111110010101111101011101010000110011111100111001110100111,
+	  0b0100010101010110000010111011110100000010011001001010001110111101,
+	  0b1101110111000010001101100000110100000111001001101011000101011011,
+	  0b0100111011101101010000001101011000101100101110010010110000101011,
+	  0b0100000110111000000110101000010011101000110100010110000011101101,
+	  0b1011001101001000100001010001100100001111011101010101110001010110,
+	  0b1000000001000000101001110010110010001111101101010101001100000110,
+	  0b0101110110100110000110000001001010111110001110010000111111010011,
+	  0b1010001111100111000100011100100100111100100101000001011001000111,
+	  0b1010011000011100110101100111001011100101111111100001110100000100,
+	  0b1100011100100010100000110001001010000000100000001001010111011101,
+	  0b0101110000100011001111101101000000100110000010010111010001111010,
+	  0b0100111100011010110111101000100110000111001001101100000001111100,
+	  0b1100100100111110101011000100000101011010110111000111110100110101,
+	  0b0110010000010111010100110011000000111010000010111011010110000100,
+	  0b0101001001010010110111010111000101011100000111100111000001110010,
+	  0b1101111111001011101010110001000111011010111101001011010110100100,
+	  0b0001000100110000011111101011001101110010110110010000000011100100,
+	  0b0001000000000101001001001000000000011000100011001110101001001110,
+	  0b0010010010001000111010011011100001000110011011011110110100111000,
+	  0b0000100110101100000111100010100100011100110111011100001111001100,
+	  0b1011111010001110001100000011110111111111100000001011111111101100,
+	  0b0000011100001111010101110000100110111100101101110111101001000001,
+	  0b1100010001110110111100001001001101101000011100000010110101001011,
+	  0b0100101001101011111001011110101101100011011111011100101010101111,
+	  0b0001101001111001110000101101101100001011010001011110011101000010,
+	  0b1111000000101001101111011010110011101110100001011011001011100010,
+	  0b0101001010111101101100001111100010010110001101001000001101100100,
+	  0b0101100101011110001100101011111000111001111001001001101101100001,
+	  0b1111001101010010100100011011000110110010001111000111010001001101,
+	  0b0001110010011000000001000110110111011000011100001000011001110111,
+	  0b0100001011011011011011110011101100100101111111101100101000001110,
+	  0b0101011110111101010111100111101111000101111111111110100011011010,
+	  0b1110101010001001110100000010110111010111111010111110100110010110,
+	  0b1010001111100001001100101000110100001100011100110010000011010111,
+	  0b1111111101101111000100111100000101011000001110011011101010111001,
+	  0b1111101100001110100101111101011001000100000101110000110010100011,
+	  0b1001010110110101101101000101010001010000101011011111010011010000,
+	  0b0111001110110011101001100111000001000100001010110000010000001101,
+	  0b0101111100111110100111011001111001111011011110010111010011101010,
+	  0b1110111000000001100100111001100100110001011011001110101111110111,
+	  0b0001010001001101010111101010011111000011110001101101011001111111,
+	  0b0101000011100011010010001101100001011101011010100110101100100010,
+	  0b0001000101011000100101111100110110000101101101111000110001001011,
+	  0b0101100101001011011000010101000000010100011100101101000010011111,
+	  0b1000010010001011101001011010100010111011110100110011011000100111,
+	  0b1000011011100001010111010111010011101100100010010010100100101001,
+	  0b1001001001010111110101000010111010000000101111010100001010010010,
+	  0b0011011110110010010101111011000001000000000011011111000011111011,
+	  0b1011000110100011001110000001000100000001011100010111010010011110,
+	  0b0111101110110101110111110000011000000100011100011000101101101110,
+	  0b1001100101111011011100011110101011001111100111101010101010110111,
+	  0b1100110010010001100011001111010000000100011101001111011101001111,
+	  0b1000111001111010100101000010000100000001001100101010001011001101,
+	  0b0011101011110000110010100101010100110010100001000010101011111101,
+	  0b1100000000000110000010101011000000011101000110011111100010111111,
+	  0b0010100110000011011100010110111100010110101100110011101110001101,
+	  0b0010111101010011111000111001111100110111111100100011110001101110,
+	  0b1001110111001001101001001001011000010100110001000000100011010110,
+	  0b0011110101100111011011111100001000011001010100111100100101111010,
+	  0b0010001101000011000010100101110000010101101000100110000100001010,
+	  0b0010000010100110010101100101110011101111000111111111001001100001,
+	  0b0100111111011011011011100111111011000010011101101111011111110110,
+	  0b1111111111010110101011101000100101110100001110001001101011100111,
+	  0b1011111101000101110000111100100010111010100001010000010010110010,
+	  0b1111010101001011101011101010000100110110001110111100100110111111,
+	  0b1011001101000001001101000010101010010110010001100001011100011010,
+	  0b0101001011011101010001110100010000010001111100100100100001001101,
+	  0b0010100000111001100011000101100101000001111100111001101000000010,
+	  0b1011001111010101011001000100100110100100110111110100000110111000,
+	  0b0101011111010011100011010010111101110010100001111111100010001001,
+	  0b0010111011101100100000000000001111111010011101100111100001001101,
+	  0b1101000000000000000000000000000000000000000000000000000000000000 };
+  };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+  struct floating_type_traits<long double>
+  {
+    static constexpr int mantissa_bits = 105;
+    static constexpr int exponent_bits = 11;
+    static constexpr bool has_implicit_leading_bit = true;
+    using mantissa_t = unsigned __int128;
+    using shortest_scientific_t = ryu::floating_decimal_128;
+
+    static constexpr uint64_t pow10_adjustment_tab[]
+      = { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	  0b0000000000000000000100000000000000000000001000000000000000000010,
+	  0b0000100000000000000000001001000000000000000001100100000000000000,
+	  0b0011000000000000000000000000000001110000010000000000000000000000,
+	  0b0000100000000000001000000000000000000000000000100000000000000000 };
+  };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+  struct ieee_t
+  {
+    typename floating_type_traits<T>::mantissa_t mantissa;
+    uint32_t biased_exponent;
+    bool sign;
+  };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+  ieee_t<T>
+  get_ieee_repr(const T value)
+  {
+    constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+    constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+    constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+    constexpr auto get_uint_t = [] {
+      if constexpr (total_bits <= 32)
+	return uint32_t{};
+      else if constexpr (total_bits <= 64)
+	return uint64_t{};
+#ifdef __SIZEOF_INT128__
+      else if constexpr (total_bits <= 128)
+	return (unsigned __int128){};
+#endif
+    };
+    using uint_t = decltype(get_uint_t());
+    uint_t value_bits = 0;
+    memcpy(&value_bits, &value, sizeof(value));
+
+    ieee_t<T> ieee_repr;
+    ieee_repr.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+    ieee_repr.biased_exponent
+      = (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+    ieee_repr.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+    return ieee_repr;
+  }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+  ieee_t<long double>
+  get_ieee_repr(const long double value)
+  {
+    // The layout of __ibm128 isn't compatible with the standard IEEE format.
+    // So we transform it into an IEEE-compatible format, suitable for
+    // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+    // mantissa (plus an implicit leading bit).  We use the exponent and sign
+    // of the high part, and we merge the mantissa of the high part with the
+    // mantissa (and the implicit leading bit) of the low part.
+    using uint_t = unsigned __int128;
+    uint_t value_bits = 0;
+    memcpy(&value_bits, &value, sizeof(value_bits));
+
+    const uint64_t value_hi = value_bits;
+    const uint64_t value_lo = value_bits >> 64;
+
+    uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+    unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+    const int sign_hi = (value_hi >> 63) & 1;
+
+    uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+    const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+    const int sign_lo = (value_lo >> 63) & 1;
+
+      {
+	// The following code for adjusting the low-part mantissa to combine
+	// it with the high-part mantissa is taken from the glibc source file
+	// sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	mantissa_lo <<= 7;
+	if (exponent_lo != 0)
+	  mantissa_lo |= (1ull << (52 + 7));
+	else
+	  mantissa_lo <<= 1;
+
+	const int ediff = exponent_hi - exponent_lo - 53;
+	if (ediff > 63)
+	  mantissa_lo = 0;
+	else if (ediff > 0)
+	  mantissa_lo >>= ediff;
+	else if (ediff < 0)
+	  mantissa_lo <<= -ediff;
+
+	if (sign_lo != sign_hi && mantissa_lo != 0)
+	  {
+	    mantissa_lo = (1ull << 60) - mantissa_lo;
+	    if (mantissa_hi == 0)
+	      {
+		mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		exponent_hi--;
+	      }
+	    else
+	      mantissa_hi--;
+	  }
+      }
+
+    ieee_t<long double> ieee_repr;
+    ieee_repr.mantissa = ((uint_t{mantissa_hi} << 64)
+			  | (uint_t{mantissa_lo} << 4)) >> 11;
+    ieee_repr.biased_exponent = exponent_hi;
+    ieee_repr.sign = sign_hi;
+    return ieee_repr;
+  }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+  typename floating_type_traits<T>::shortest_scientific_t
+  floating_to_shortest_scientific(const T value)
+  {
+    if constexpr (std::is_same_v<T, float>)
+      return ryu::floating_to_fd32(value);
+    else if constexpr (std::is_same_v<T, double>)
+      return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+    else if constexpr (std::is_same_v<T, long double>)
+      {
+	constexpr int mantissa_bits
+	  = floating_type_traits<T>::mantissa_bits;
+	constexpr int exponent_bits
+	  = floating_type_traits<T>::exponent_bits;
+	constexpr bool has_implicit_leading_bit
+	  = floating_type_traits<T>::has_implicit_leading_bit;
+
+	const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+					      mantissa_bits, exponent_bits,
+					      !has_implicit_leading_bit);
+      }
+#endif
+  }
+
+  // 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.0 has the shortest scientific form 1e23, so its
+  // exact value is smaller than its shortest scientific form.
+  //
+  // Usually we can rely on the shortest scientific exponent to determine the
+  // length of the exact fixed form, but for these powers of 10 the length of
+  // the fixed form is one less than what the scientific exponent suggests.
+  //
+  // This subroutine inspects a lookup table to detect when fd is such a
+  // "rounded up" power of 10.
+  template<typename T>
+  bool
+  is_rounded_up_pow10_p(const typename
+			floating_type_traits<T>::shortest_scientific_t fd)
+  {
+    if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+      return false;
+
+    constexpr auto& pow10_adjustment_tab
+      = floating_type_traits<T>::pow10_adjustment_tab;
+    __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+    return (pow10_adjustment_tab[fd.exponent/64]
+	    & (1ull << (63 - fd.exponent%64)));
+  }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+			       FP_ZERO, 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())
+    {
+      // We're formatting +-inf or +-nan.
+      if (!__builtin_signbit(value))
+	str.remove_prefix(strlen("-"));
+
+      if (last - first < (int)str.length())
+	return {{last, errc::value_too_large}};
+
+      memcpy(first, &str[0], str.length());
+      first += str.length();
+      return {{first, errc{}}};
+    }
+
+  // We're formatting 0.
+  __glibcxx_assert(value == 0);
+  const bool sign = __builtin_signbit(value);
+  int expected_output_length;
+  const char* orig_first = first;
+  switch (fmt)
+    {
+    case chars_format::fixed:
+    case chars_format::scientific:
+    case chars_format::hex:
+      expected_output_length = sign + 1;
+      if (precision)
+	expected_output_length += strlen(".") + precision;
+      if (fmt == chars_format::scientific)
+	expected_output_length += strlen("e+00");
+      else if (fmt == chars_format::hex)
+	expected_output_length += strlen("p+0");
+      if (last - first < expected_output_length)
+	return {{last, errc::value_too_large}};
+
+      if (sign)
+	*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;
+	}
+      break;
+
+    case chars_format::general:
+    default: // case chars_format{}:
+      expected_output_length = sign + 1;
+      if (last - first < expected_output_length)
+	return {{last, errc::value_too_large}};
+
+      if (sign)
+	*first++ = '-';
+      *first++ = '0';
+      break;
+    }
+  __glibcxx_assert(first - orig_first == expected_output_length);
+  return {{first, errc{}}};
+}
+
+// This subroutine of the floating-point to_chars overloads performs
+// hexadecimal formatting.
+template<typename T>
+static to_chars_result
+__floating_to_chars_hex(char* first, char* const last, const T value,
+			const optional<int> precision)
+{
+  if (precision.has_value() && precision.value() < 0) [[unlikely]]
+    // A negative precision argument is treated as if it were omitted.
+    return __floating_to_chars_hex(first, last, value, nullopt);
+
+  __glibcxx_requires_valid_range(first, last);
+
+  constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+  constexpr bool has_implicit_leading_bit
+    = floating_type_traits<T>::has_implicit_leading_bit;
+  constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+  constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+  using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+  constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+  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.
+  const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+  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;
+  static_assert(mantissa_t_width >= rounded_mantissa_bits);
+  mantissa_t effective_mantissa
+    = ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+  if (is_normal_number)
+    {
+      if constexpr (has_implicit_leading_bit)
+	// Restore the mantissa's implicit leading bit.
+	effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+      else
+	// The explicit mantissa bit should already be set.
+	__glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								 - 1u)));
+    }
+
+  // Compute the shortest precision needed to print this value exactly,
+  // disregarding trailing zeros.
+  constexpr int full_hex_precision = (has_implicit_leading_bit
+				      ? (mantissa_bits + 3) / 4
+				      // With an explicit leading bit, we
+				      // use the four leading nibbles as the
+				      // hexit before the decimal point.
+				      : (mantissa_bits - 4 + 3) / 4);
+  const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+  const int shortest_full_precision = full_hex_precision - trailing_zeros;
+  __glibcxx_assert(shortest_full_precision >= 0);
+
+  int written_exponent = unbiased_exponent;
+  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 = mantissa_t;
+      const bitvec round_bit = effective_mantissa << 1;
+      const bitvec has_tail_bits = round_bit - 1;
+      const bitvec lsb_bit = effective_mantissa;
+      const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+      const int dropped_bits = 4*(full_hex_precision - effective_precision);
+      // Mask out the dropped nibbles.
+      effective_mantissa >>= dropped_bits;
+      effective_mantissa <<= dropped_bits;
+      if (should_round & (mantissa_t{1} << dropped_bits))
+	{
+	  // Round up the least significant nibble.
+	  effective_mantissa += mantissa_t{1} << dropped_bits;
+	  // Check and adjust for overflow of the leading nibble.  When the
+	  // type has an implicit leading bit, then the leading nibble
+	  // before rounding is either 0 or 1, so it can't overflow.
+	  if constexpr (!has_implicit_leading_bit)
+	    {
+	      // The only supported floating-point type with explicit
+	      // leading mantissa bit is LDK_FLOAT80, i.e. x86 80-bit
+	      // extended precision, and so we hardcode the below overflow
+	      // check+adjustment for this type.
+	      static_assert(mantissa_t_width == 64
+			    && rounded_mantissa_bits == 64);
+	      if (effective_mantissa == 0)
+		{
+		  // We rounded up the least significant nibble and the
+		  // mantissa overflowed, e.g f.fcp+10 with precision=1
+		  // became 10.0p+10.  Absorb this extra hexit into the
+		  // exponent to obtain 1.0p+14.
+		  effective_mantissa
+		    = mantissa_t{1} << (rounded_mantissa_bits - 4);
+		  written_exponent += 4;
+		}
+	    }
+	}
+    }
+
+  // Compute the leading hexit and mask it out from the mantissa.
+  char leading_hexit;
+  if constexpr (has_implicit_leading_bit)
+    {
+      const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+      __glibcxx_assert(nibble <= 2);
+      leading_hexit = '0' + nibble;
+      effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+    }
+  else
+    {
+      const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+      __glibcxx_assert(nibble < 16);
+      leading_hexit = "0123456789abcdef"[nibble];
+      effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+      written_exponent -= 3;
+    }
+
+  // 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_written_exponent = abs(written_exponent);
+  expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+			     : abs_written_exponent >= 1000 ? strlen("p+dddd")
+			     : abs_written_exponent >= 100 ? strlen("p+ddd")
+			     : abs_written_exponent >= 10 ? strlen("p+dd")
+			     : strlen("p+d"));
+  if (last - first < expected_output_length)
+    return {last, errc::value_too_large};
+
+  const auto saved_first = 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.
+      int nibble_offset = rounded_mantissa_bits;
+      if constexpr (!has_implicit_leading_bit)
+	// We already printed the entire leading hexit.
+	nibble_offset -= 4;
+      while (effective_mantissa != 0)
+	{
+	  nibble_offset -= 4;
+	  const unsigned nibble = effective_mantissa >> nibble_offset;
+	  __glibcxx_assert(nibble < 16);
+	  *first++ = "0123456789abcdef"[nibble];
+	  ++written_hexits;
+	   effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	}
+      __glibcxx_assert(nibble_offset >= 0);
+      __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 (written_exponent >= 0)
+    *first++ = '+';
+  const to_chars_result result = to_chars(first, last, written_exponent);
+  __glibcxx_assert(result.ec == errc{}
+		   && result.ptr == saved_first + expected_output_length);
+  return result;
+}
+
+template<typename T>
+static to_chars_result
+__floating_to_chars_shortest(char* first, char* const 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 = get_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 will be 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(".");
+      const int abs_exponent = abs(scientific_exponent);
+      expected_output_length += (abs_exponent >= 1000 ? strlen("e+dddd")
+				 : abs_exponent >= 100 ? strlen("e+ddd")
+				 : strlen("e+dd"));
+      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, and so 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 than what the
+      // shortest scientific form given by Ryu provides.
+      //
+      // 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_rounded_up_pow10_p<T>(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...
+      //
+      // After adding some wiggle room due to rounding we get the condition
+      // value_fits_inside_mantissa_p below.
+      const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+      const bool value_fits_inside_mantissa_p
+	= (log2_mantissa + (fd.exponent*10 + 2) / 3
+	   < floating_type_traits<T>::mantissa_bits - 2);
+      if (value_fits_inside_mantissa_p)
+	{
+	  // 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 = fd.sign + (result.ptr - first);
+	  __glibcxx_assert(output_length == expected_output_length);
+	  return result;
+	}
+      else if constexpr (is_same_v<T, long double>)
+	{
+	  // We can't use d2fixed_buffered_n for types larger than double,
+	  // so we instead format larger types through sprintf.
+	  // TODO: We currently go through an intermediate buffer in order
+	  // to accomodate the mandatory null terminator of sprintf, but we
+	  // can avoid this if we use sprintf to write all but the last
+	  // digit, and carefully compute and write the last digit
+	  // ourselves.
+	  char buffer[expected_output_length+1];
+#if _GLIBCXX_USE_C99_FENV_TR1
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+#endif
+	  const int output_length = sprintf(buffer, "%.0Lf", value);
+#if _GLIBCXX_USE_C99_FENV_TR1
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+#endif
+	  __glibcxx_assert(output_length == expected_output_length);
+	  memcpy(first, buffer, output_length);
+	  return {first + output_length, errc{}};
+	}
+      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 = 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 in the correct spot.
+	  memmove(&result.ptr[fd.exponent+1], &result.ptr[fd.exponent],
+		  -fd.exponent);
+	  result.ptr[fd.exponent] = '.';
+	  const int output_length = result.ptr + 1 - orig_first;
+	  __glibcxx_assert(output_length == expected_output_length);
+	  ++result.ptr;
+	  return result;
+	}
+    }
+
+  __glibcxx_assert(false);
+  __builtin_unreachable();
+}
+
+template<typename T>
+static to_chars_result
+__floating_to_chars_precision(char* first, char* const last, const T value,
+			      chars_format fmt, const int precision)
+{
+  if (fmt == chars_format::hex)
+    return __floating_to_chars_hex(first, last, value, precision);
+
+  if (precision < 0) [[unlikely]]
+    // 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.
+    return __floating_to_chars_precision(first, last, value, fmt, 6);
+
+  __glibcxx_assert(fmt == chars_format::fixed
+		   || fmt == chars_format::scientific
+		   || fmt == chars_format::general);
+  __glibcxx_requires_valid_range(first, last);
+
+  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;
+
+  // Extract the sign and exponent from the value.
+  const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+  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.
+  // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+  // bounds below is necessary only for __ibm128, it seems.  Even though the
+  // type has 105 bits of precision, printf may output 106 fractional digits
+  // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+  const int max_eff_scientific_precision
+    = (floor_log2_value >= 0
+       ? max(mantissa_bits + 1, approx_log10_value + 1)
+       : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+  __glibcxx_assert(max_eff_scientific_precision > 0);
+
+  const int max_eff_fixed_precision
+    = (floor_log2_value >= 0
+       ? mantissa_bits + 1
+       : -floor_log2_value + mantissa_bits + 1);
+  __glibcxx_assert(max_eff_fixed_precision > 0);
+
+  // Ryu doesn't support formatting floating-point types larger than double
+  // with an explicit precision, so instead we just go through printf.
+  if constexpr (is_same_v<T, long double>)
+    {
+      int effective_precision;
+      const char* output_specifier;
+      if (fmt == chars_format::scientific)
+	{
+	  effective_precision = min(precision, max_eff_scientific_precision);
+	  output_specifier = "%.*Le";
+	}
+      else if (fmt == chars_format::fixed)
+	{
+	  effective_precision = min(precision, max_eff_fixed_precision);
+	  output_specifier = "%.*Lf";
+	}
+      else if (fmt == chars_format::general)
+	{
+	  effective_precision = min(precision, max_eff_scientific_precision);
+	  output_specifier = "%.*Lg";
+	}
+      const int excess_precision = (fmt != chars_format::general
+				    ? precision - effective_precision : 0);
+
+      // Since the output of printf is locale-sensitive, we need to be able
+      // to handle a radix point that's different from '.'.
+      char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+      if (effective_precision > 0)
+	// ???: Can nl_langinfo() ever return null?
+	if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	  {
+	    strncpy(radix, radix_ptr, sizeof(radix)-1);
+	    // We accept only radix points which are at most 4 bytes (one
+	    // UTF-8 character) wide.
+	    __glibcxx_assert(radix[4] == '\0');
+	  }
+
+      // Compute straightforward upper bounds on the output length.
+      int output_length_upper_bound;
+      if (fmt == chars_format::scientific || fmt == chars_format::general)
+	output_length_upper_bound = (strlen("-d") + sizeof(radix)
+				     + effective_precision
+				     + strlen("e+dddd"));
+      else if (fmt == chars_format::fixed)
+	{
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  output_length_upper_bound += sizeof(radix) + effective_precision;
+	}
+
+      // Do the sprintf into the local buffer.
+      char buffer[output_length_upper_bound+1];
+#if _GLIBCXX_USE_C99_FENV_TR1
+      const int saved_rounding_mode = fegetround();
+      if (saved_rounding_mode != FE_TONEAREST)
+	fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+#endif
+      int output_length
+	= sprintf(buffer, output_specifier, effective_precision, value);
+#if _GLIBCXX_USE_C99_FENV_TR1
+      if (saved_rounding_mode != FE_TONEAREST)
+	fesetround(saved_rounding_mode);
+#endif
+      __glibcxx_assert(output_length <= output_length_upper_bound);
+
+      if (effective_precision > 0)
+	// We need to replace a radix that is different from '.' with '.'.
+	if (const string_view radix_sv = {radix}; radix_sv != ".")
+	  {
+	    const string_view buffer_sv = {buffer, (size_t)output_length};
+	    const size_t radix_index = buffer_sv.find(radix_sv);
+	    if (radix_index != string_view::npos)
+	      {
+		buffer[radix_index] = '.';
+		if (radix_sv.length() > 1)
+		  {
+		    memmove(&buffer[radix_index + 1],
+			    &buffer[radix_index + radix_sv.length()],
+			    output_length - radix_index - radix_sv.length());
+		    output_length -= radix_sv.length() - 1;
+		  }
+	      }
+	  }
+
+      // Copy the string from the buffer over to the output range.
+      if (last - first < output_length + excess_precision)
+	return {last, errc::value_too_large};
+      memcpy(first, buffer, output_length);
+      first += output_length;
+
+      // Add the excess 0s to the result.
+      if (excess_precision > 0)
+	{
+	  if (fmt == chars_format::scientific)
+	    {
+	      char* const significand_end
+		= (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		   : 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;
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      memset(first, '0', excess_precision);
+	      first += excess_precision;
+	    }
+	}
+      return {first, errc{}};
+    }
+  else 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 enough away from +-100.  But if it's
+      // near +-100, then our log2 approximation is too coarse (and doesn't
+      // consider precision-dependent rounding) in order to accurately
+      // distinguish between a scientific exponent of +-100 and +-99.
+      const bool scientific_exponent_near_100_p
+	= abs(abs(floor_log2_value) - 332) <= 4;
+
+      // Compute an upper bound on the output length.  TODO: Maybe also
+      // consider a lower bound on the output length.
+      int output_length_upper_bound = sign + strlen("d");
+      if (effective_precision > 0)
+	output_length_upper_bound += strlen(".") + effective_precision;
+      if (scientific_exponent_near_100_p
+	  || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	output_length_upper_bound += strlen("e+ddd");
+      else
+	output_length_upper_bound += strlen("e+dd");
+
+      int output_length;
+      if (last - first >= output_length_upper_bound + excess_precision)
+	{
+	  // The result will definitely fit into the output range, so we can
+	  // write directly into it.
+	  output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						first, nullptr);
+	  __glibcxx_assert(output_length == output_length_upper_bound
+			   || (scientific_exponent_near_100_p
+			       && (output_length
+				   == output_length_upper_bound - 1)));
+	}
+      else if (scientific_exponent_near_100_p)
+	{
+	  // Write the result of d2exp_buffered_n into an intermediate
+	  // buffer, do a bounds check, and copy the result into the output
+	  // range.
+	  char buffer[output_length_upper_bound];
+	  output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						buffer, nullptr);
+	  __glibcxx_assert(output_length == output_length_upper_bound - 1
+			   || output_length == output_length_upper_bound);
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	}
+      else
+	// If the scientific exponent is not near 100, then the upper bound
+	// is actually the exact length, and so the result will definitely
+	// not fit into the output range.
+	return {last, errc::value_too_large};
+      first += output_length;
+      if (excess_precision > 0)
+	{
+	  // Splice the excess zeros into 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;
+
+      // Compute an upper bound on the output length.  TODO: Maybe also
+      // consider a lower bound on the output length.
+      int output_length_upper_bound;
+      if (approx_log10_value >= 0)
+	output_length_upper_bound = sign + approx_log10_value + 1;
+      else
+	output_length_upper_bound = sign + strlen("0");
+      if (effective_precision > 0)
+	output_length_upper_bound += strlen(".") + effective_precision;
+
+      int output_length;
+      if (last - first >= output_length_upper_bound + excess_precision)
+	{
+	  // The result will definitely fit into the output range, so we can
+	  // write directly into it.
+	  output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						  first);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+	}
+      else
+	{
+	  // Write the result of d2fixed_buffered_n into an intermediate
+	  // buffer, do a bounds check, and copy the result into the output
+	  // range.
+	  char buffer[output_length_upper_bound];
+	  output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						  buffer);
+	  __glibcxx_assert(output_length <= output_length_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_length_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 form such as 1.00e-2 into the equivalent
+      // fixed form 0.001.
+      char buffer[4 + output_length_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_length_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_length_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{}};
+    }
+
+  __glibcxx_assert(false);
+  __builtin_unreachable();
+}
+
+// Define the overloads for float.
+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, float 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,
+	 int precision) noexcept
+{ return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+// Define the overloads for double.
+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, double 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); }
+
+// Define the overloads for long double.
+to_chars_result
+to_chars(char* first, char* last, long double value) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_shortest(first, last, double(value),
+					chars_format{});
+  else
+    return __floating_to_chars_shortest(first, last, value, chars_format{});
+}
+
+to_chars_result
+to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_shortest(first, last, double(value), fmt);
+  else
+    return __floating_to_chars_shortest(first, last, value, fmt);
+}
+
+to_chars_result
+to_chars(char* first, char* last, long double value, chars_format fmt,
+	 int precision) noexcept
+{
+  if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		|| LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+    return __floating_to_chars_precision(first, last, double(value), fmt,
+					 precision);
+  else
+    return __floating_to_chars_precision(first, last, value, fmt, precision);
+}
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+// Map the -mlong-double-64 long double overloads to the double overloads.
+extern "C" to_chars_result
+_ZSt8to_charsPcS_e(char* first, char* last, double value) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_d")));
+
+extern "C" to_chars_result
+_ZSt8to_charsPcS_eSt12chars_format(char* first, char* last, double value,
+				   chars_format fmt) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_format")));
+
+extern "C" to_chars_result
+_ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
+				    chars_format fmt, int precision) noexcept
+  __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_formati")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..12ac8ae7822
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,199 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// <charconv> is supported in C++14 as a GNU extension, but this test uses C++17
+// hexadecimal floating-point literals.
+// { dg-do run { target c++17 } }
+// { dg-xfail-run-if "Ryu needs __int128" { large_long_double && { ! int128 } } }
+
+#include <charconv>
+
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Verify that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-22 16:00 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-22 16:00 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:6ad582ecb87d76f029b722a0231f8fb2b88b6429

commit 6ad582ecb87d76f029b722a0231f8fb2b88b6429
Author: Patrick Palka <ppalka@redhat.com>
Date:   Tue Jul 21 08:37:18 2020 -0400

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing explicit-precision
    fixed and scientific formatting of float and double. For
    explicit-precision formatting of long double we fall back to using
    printf.  Hexadecimal formatting for float, double and long double is
    implemented from scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the string a
    second time when the general formatting mode resolves to fixed.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's approach of being consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form of 1.08p+0 is 2.1p-1, but we output the former instead of the
       latter, as does printf.
    
    2. The Ryu routines for doing shortest formatting on types larger than
       binary64 use the __int128 type, and some targets (e.g. i386) have a
       large long double type but lack __int128.  For such targets we make
       the long double to_chars overloads go through the double overloads,
       which means we lose precision in the output.  (The mantissa of long
       double is 64 bits on i386, so I think we could potentially fix this
       by writing a specialized version of the generic Ryu formatting
       routine which works with uint64_t instead of __int128.)
    
    3. The __ibm128 shortest formatting routines don't guarantee
       round-trippability if the difference between the high- and low-order
       exponent is too large.  This is because we treat the type as if it
       has a contiguous 105-bit mantissa by merging the high- and low-order
       mantissas, so we potentially lose precision from the low-order part.
       Although this precision-dropping behavior is non-conforming, it seems
       consistent with how printf formats __ibm128.
    
    libstdc++-v3/ChangeLog:
    
            * config/abi/pre/gnu.ver: Add new exports.
            * include/std/charconv (to_chars): Declare the floating-point
            overloads for float, double and long 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.
            (to_chars): Define for float, double and long double.
            * testsuite/20_util/to_chars/long_double.cc: New test.

Diff:
---
 libstdc++-v3/config/abi/pre/gnu.ver                |    9 +
 libstdc++-v3/include/std/charconv                  |   24 +
 libstdc++-v3/src/c++17/Makefile.am                 |    1 +
 libstdc++-v3/src/c++17/Makefile.in                 |    3 +-
 libstdc++-v3/src/c++17/floating_to_chars.cc        | 1552 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 6 files changed, 1785 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 17aff5d907b..9be022d319a 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2304,6 +2304,15 @@ GLIBCXX_3.4.29 {
     # std::from_chars
     _ZSt10from_charsPKcS0_R[def]St12chars_format;
 
+    # std::to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[defg];
+
+    # std::to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[defg]St12chars_format;
+
+    # std::to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[defg]St12chars_formati;
+
 } GLIBCXX_3.4.28;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index be668c1939e..b0f8a6106bc 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -702,6 +702,30 @@ namespace __detail
 	     chars_format __fmt = chars_format::general);
 #endif
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long 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 642efb976ac..0f5b7eb8e56 100644
--- a/libstdc++-v3/src/c++17/Makefile.am
+++ b/libstdc++-v3/src/c++17/Makefile.am
@@ -51,6 +51,7 @@ endif
 
 sources = \
 	floating_from_chars.cc \
+	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 ce08eb3ff11..bf291d7f236 100644
--- a/libstdc++-v3/src/c++17/Makefile.in
+++ b/libstdc++-v3/src/c++17/Makefile.in
@@ -124,7 +124,7 @@ 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 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
+am__objects_2 = floating_from_chars.lo 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 \
@@ -436,6 +436,7 @@ headers =
 
 sources = \
 	floating_from_chars.cc \
+	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..be8784336b7
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1552 @@
+// std::to_chars implementation for floating-point types -*- 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_ASSERTIONS
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We currently support the binary64, float80 (i.e. x86 80-bit extended
+// precision), binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#  define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_repr;
+      ieee_repr.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_repr.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_repr.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_repr;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (plus an implicit leading bit).  We use the exponent and sign
+      // of the high part, and we merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_repr;
+      ieee_repr.mantissa = ((uint_t{mantissa_hi} << 64)
+			    | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_repr.biased_exponent = exponent_hi;
+      ieee_repr.sign = sign_hi;
+      return ieee_rep;
+    }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    typename floating_type_traits<T>::shortest_scientific_t
+    floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (std::is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (std::is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (std::is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						mantissa_bits, exponent_bits,
+						!has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  // 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.0 has the shortest scientific form 1e23, so its
+  // exact value is smaller than its shortest scientific form.
+  //
+  // Usually we can rely on the shortest scientific exponent to determine the
+  // length of the exact fixed form, but for these powers of 10 the length of
+  // the fixed form is one less than what the scientific exponent suggests.
+  //
+  // This subroutine inspects a lookup table to detect when fd is such a
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+				   FP_ZERO, 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())
+	{
+	  // We're formatting +-inf or +-nan.
+	  if (!__builtin_signbit(value))
+	    str.remove_prefix(strlen("-"));
+
+	  if (last - first < (int)str.length())
+	    return {{last, errc::value_too_large}};
+
+	  memcpy(first, &str[0], str.length());
+	  first += str.length();
+	  return {{first, errc{}}};
+	}
+
+      // We're formatting 0.
+      __glibcxx_assert(value == 0);
+      const bool sign = __builtin_signbit(value);
+      int expected_output_length;
+      const char* orig_first = first;
+      switch (fmt)
+	{
+	case chars_format::fixed:
+	case chars_format::scientific:
+	case chars_format::hex:
+	  expected_output_length = sign + 1;
+	  if (precision)
+	    expected_output_length += strlen(".") + precision;
+	  if (fmt == chars_format::scientific)
+	    expected_output_length += strlen("e+00");
+	  else if (fmt == chars_format::hex)
+	    expected_output_length += strlen("p+0");
+	  if (last - first < expected_output_length)
+	    return {{last, errc::value_too_large}};
+
+	  if (sign)
+	    *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;
+	    }
+	  break;
+
+	case chars_format::general:
+	default: // case chars_format{}:
+	  expected_output_length = sign + 1;
+	  if (last - first < expected_output_length)
+	    return {{last, errc::value_too_large}};
+
+	  if (sign)
+	    *first++ = '-';
+	  *first++ = '0';
+	  break;
+	}
+      __glibcxx_assert(first - orig_first == expected_output_length);
+      return {{first, errc{}}};
+    }
+
+  // This subroutine of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_hex(char* first, char* const last, const T value,
+			    const optional<int> precision)
+    {
+      if (precision && *precision < 0) [[unlikely]]
+	// A negative precision argument is treated as if it is omitted.
+	return __floating_to_chars_hex(first, last, value, nullopt);
+
+      __glibcxx_requires_valid_range(first, last);
+
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr bool has_implicit_leading_bit
+	= floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+      constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+      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.
+      const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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;
+      static_assert(mantissa_t_width >= rounded_mantissa_bits);
+      mantissa_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								     - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = mantissa_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits;
+	  effective_mantissa <<= dropped_bits;
+	  if (should_round & (mantissa_t{1} << dropped_bits))
+	    {
+	      // Round up the least significant nibble.
+	      effective_mantissa += mantissa_t{1} << dropped_bits;
+	      // Check and adjust for overflow of the leading nibble.  When the
+	      // type has an implicit leading bit, then the leading nibble
+	      // before rounding is either 0 or 1, so it can't overflow.
+	      if constexpr (!has_implicit_leading_bit)
+		{
+		  // The only supported floating-point type with explicit
+		  // leading mantissa bit is LDK_FLOAT80, i.e. x86 80-bit
+		  // extended precision, and so we hardcode the below overflow
+		  // check+adjustment for this type.
+		  static_assert(mantissa_t_width == 64
+				&& rounded_mantissa_bits == 64);
+		  if (effective_mantissa == 0)
+		    {
+		      // We rounded up the least significant nibble and the
+		      // mantissa overflowed, e.g f.fcp+10 with precision=1
+		      // became 10.0p+10.  Absorb this extra hexit into the
+		      // exponent to obtain 1.0p+14.
+		      effective_mantissa
+			= mantissa_t{1} << (rounded_mantissa_bits - 4);
+		      written_exponent += 4;
+		    }
+		}
+	    }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_written_exponent = abs(written_exponent);
+      expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_written_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_written_exponent >= 100 ? strlen("p+ddd")
+				 : abs_written_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      if (last - first < expected_output_length)
+	return {last, errc::value_too_large};
+
+      const auto saved_first = 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.
+	  int nibble_offset = rounded_mantissa_bits;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      nibble_offset -= 4;
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+	       effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	    }
+	  __glibcxx_assert(nibble_offset >= 0);
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      const to_chars_result result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_shortest(char* first, char* const 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 = get_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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+dddd")
+				     : abs_exponent >= 100 ? strlen("e+ddd")
+				     : strlen("e+dd"));
+	  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, and so 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 than what the
+	  // shortest scientific form given by Ryu provides.
+	  //
+	  // 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_rounded_up_pow10_p<T>(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...
+	  //
+	  // After adding some wiggle room due to rounding we get the condition
+	  // value_fits_inside_mantissa_p below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa_p
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_fits_inside_mantissa_p)
+	    {
+	      // 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 = fd.sign + (result.ptr - first);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      return result;
+	    }
+	  else if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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 = 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 in the correct spot.
+	      memmove(&result.ptr[fd.exponent+1], &result.ptr[fd.exponent],
+		      -fd.exponent);
+	      result.ptr[fd.exponent] = '.';
+	      const int output_length = result.ptr + 1 - orig_first;
+	      __glibcxx_assert(output_length == expected_output_length);
+	      ++result.ptr;
+	      return result;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_precision(char* first, char* const 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;
+
+      // Extract the sign and exponent from the value.
+      const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+      // Ryu doesn't support formatting floating-point types larger than double
+      // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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 enough away from +-100.  But if it's
+	  // near +-100, then our log2 approximation is too coarse (and doesn't
+	  // consider precision-dependent rounding) in order to accurately
+	  // distinguish between a scientific exponent of +-100 and +-99.
+	  const bool scientific_exponent_near_100_p
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound = sign + strlen("d");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  if (scientific_exponent_near_100_p
+	      || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	    output_length_upper_bound += strlen("e+ddd");
+	  else
+	    output_length_upper_bound += strlen("e+dd");
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    first, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound
+			       || (scientific_exponent_near_100_p
+				   && (output_length
+				       == output_length_upper_bound - 1)));
+	    }
+	  else if (scientific_exponent_near_100_p)
+	    {
+	      // Write the result of d2exp_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    buffer, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound - 1
+			       || output_length == output_length_upper_bound);
+	      if (last - first < output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      memcpy(first, buffer, output_length);
+	    }
+	  else
+	    // If the scientific exponent is not near 100, then the upper bound
+	    // is actually the exact length, and so the result will definitely
+	    // not fit into the output range.
+	    return {last, errc::value_too_large};
+	  first += output_length;
+	  if (excess_precision > 0)
+	    {
+	      // Splice the excess zeros into 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;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      // Write the result of d2fixed_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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 form such as 1.00e-2 into the equivalent
+	  // fixed form 0.001.
+	  char buffer[4 + output_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  // Define the overloads for float.
+  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, float 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,
+	   int precision) noexcept
+  { return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+  // Define the overloads for double.
+  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, double 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); }
+
+  // Define the overloads for long double.
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value),
+					  chars_format{});
+    else
+      return __floating_to_chars_shortest(first, last, value, chars_format{});
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value), fmt);
+    else
+      return __floating_to_chars_shortest(first, last, value, fmt);
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_precision(first, last, double(value), fmt,
+					   precision);
+    else
+      return __floating_to_chars_precision(first, last, value, fmt, precision);
+  }
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+  // Map the -mlong-double-64 long double overloads to the double overloads.
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_e(char* first, char* last, double value) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_d")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_format(char* first, char* last, double value,
+				     chars_format fmt) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_format")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
+				      chars_format fmt, int precision) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_formati")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..d995aa02d09
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-22  3:10 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-22  3:10 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:4501adf2dfa0b393ec3048ad053454b64ff5f29e

commit 4501adf2dfa0b393ec3048ad053454b64ff5f29e
Author: Patrick Palka <ppalka@redhat.com>
Date:   Tue Jul 21 08:37:18 2020 -0400

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing explicit-precision
    fixed and scientific formatting of float and double. For
    explicit-precision formatting of long double we fall back to using
    printf.  Hexadecimal formatting for float, double and long double is
    implemented from scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the string a
    second time when the general formatting mode resolves to fixed.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's approach of being consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form of 1.08p+0 is 2.1p-1, but we output the former instead of the
       latter, as does printf.
    
    2. The Ryu routines for doing shortest formatting on types larger than
       binary64 use the __int128 type, and some targets (e.g. i386) have a
       large long double type but lack __int128.  For such targets we make
       the long double to_chars overloads go through the double overloads,
       which means we lose precision in the output.  (The mantissa of long
       double is 64 bits on i386, so I think we could potentially fix this
       by writing a specialized version of the generic Ryu formatting
       routine which works with uint64_t instead of __int128.)
    
    3. The __ibm128 shortest formatting routines don't guarantee
       round-trippability if the difference between the high- and low-order
       exponent is too large.  This is because we treat the type as if it
       has a contiguous 105-bit mantissa by merging the high- and low-order
       mantissas, so we potentially lose precision from the low-order part.
       Although this precision-dropping behavior is non-conforming, it seems
       consistent with how printf formats __ibm128.
    
    libstdc++-v3/ChangeLog:
    
            * config/abi/pre/gnu.ver: Add new exports.
            * include/std/charconv (to_chars): Declare the floating-point
            overloads for float, double and long 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.
            (to_chars): Define for float, double and long double.
            * testsuite/20_util/to_chars/long_double.cc: New test.

Diff:
---
 libstdc++-v3/config/abi/pre/gnu.ver                |    9 +
 libstdc++-v3/include/std/charconv                  |   24 +
 libstdc++-v3/src/c++17/Makefile.am                 |    1 +
 libstdc++-v3/src/c++17/Makefile.in                 |    3 +-
 libstdc++-v3/src/c++17/floating_to_chars.cc        | 1547 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 6 files changed, 1780 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 17aff5d907b..dbedd85068e 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2304,6 +2304,15 @@ GLIBCXX_3.4.29 {
     # std::from_chars
     _ZSt10from_charsPKcS0_R[def]St12chars_format;
 
+    # std::to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[fdeg];
+
+    # std::to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[fdeg]St12chars_format;
+
+    # std::to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[fdeg]St12chars_formati;
+
 } GLIBCXX_3.4.28;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index be668c1939e..b0f8a6106bc 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -702,6 +702,30 @@ namespace __detail
 	     chars_format __fmt = chars_format::general);
 #endif
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long 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 642efb976ac..0f5b7eb8e56 100644
--- a/libstdc++-v3/src/c++17/Makefile.am
+++ b/libstdc++-v3/src/c++17/Makefile.am
@@ -51,6 +51,7 @@ endif
 
 sources = \
 	floating_from_chars.cc \
+	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 ce08eb3ff11..bf291d7f236 100644
--- a/libstdc++-v3/src/c++17/Makefile.in
+++ b/libstdc++-v3/src/c++17/Makefile.in
@@ -124,7 +124,7 @@ 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 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
+am__objects_2 = floating_from_chars.lo 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 \
@@ -436,6 +436,7 @@ headers =
 
 sources = \
 	floating_from_chars.cc \
+	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..eb7cdf13e8b
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1547 @@
+// 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_ASSERTIONS
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We currently support the binary64, float80 (i.e. x86 80-bit extended
+// precision), binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#  define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_rep;
+      ieee_rep.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_rep.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_rep.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_rep;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (with an implicit leading bit).  We keep the exponent and sign
+      // of the high part, and merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_rep;
+      ieee_rep.mantissa = ((uint_t{mantissa_hi} << 64)
+			   | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_rep.biased_exponent = exponent_hi;
+      ieee_rep.sign = sign_hi;
+      return ieee_rep;
+    }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    typename floating_type_traits<T>::shortest_scientific_t
+    floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (std::is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (std::is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (std::is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						mantissa_bits, exponent_bits,
+						!has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  // 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.0 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
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+				   FP_ZERO, 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())
+	{
+	  if (!__builtin_signbit(value))
+	    str.remove_prefix(strlen("-"));
+
+	  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 = __builtin_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 of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_hex(char* first, char* last, const T value,
+			    optional<int> precision)
+    {
+      __glibcxx_requires_valid_range(first, last);
+
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr bool has_implicit_leading_bit
+	= floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+      constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+      if (precision && *precision < 0)
+	precision = nullopt;
+
+      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.
+      const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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;
+      static_assert(mantissa_t_width >= rounded_mantissa_bits);
+      mantissa_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								     - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = mantissa_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits;
+	  effective_mantissa <<= dropped_bits;
+	  if (should_round & (mantissa_t{1} << dropped_bits))
+	    {
+	      // Round up the least significant nibble.
+	      effective_mantissa += mantissa_t{1} << dropped_bits;
+	      // Check and adjust for overflow of the leading nibble.  When the
+	      // type has an implicit leading bit, then the leading nibble
+	      // before rounding is either 0 or 1, so it can't overflow.
+	      if constexpr (!has_implicit_leading_bit)
+		{
+		  // The only floating type with explicit leading mantissa bit
+		  // that we currently support is x86 80-bit extended precision.
+		  // So we hardcode the below overflow check for this type.
+		  static_assert(mantissa_t_width == 64
+				&& rounded_mantissa_bits == 64);
+		  if (effective_mantissa == 0)
+		    {
+		      // We rounded up the least significant nibble and the
+		      // mantissa overflowed, e.g f.fcp+10 with precision=1
+		      // became 10.0p+10.  Absorb the extra hexit into the
+		      // exponent to obtain 1.0p+14.
+		      effective_mantissa
+			= mantissa_t{1} << (rounded_mantissa_bits - 4);
+		      written_exponent += 4;
+		    }
+		}
+	    }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_written_exponent = abs(written_exponent);
+      expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_written_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_written_exponent >= 100 ? strlen("p+ddd")
+				 : abs_written_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      if (last - first < expected_output_length)
+	return {last, errc::value_too_large};
+
+      const auto saved_first = 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.
+	  int nibble_offset = rounded_mantissa_bits - 4;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+
+	       effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	       nibble_offset -= 4;
+	    }
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      const to_chars_result result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  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 = get_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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+NNNN")
+				     : abs_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, and so 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_rounded_up_pow10_p<T>(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
+	  // value_fits_inside_mantissa below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_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 = fd.sign + (result.ptr - first);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      return result;
+	    }
+	  else if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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 = 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 = result.ptr + 1 - orig_first;
+	      __glibcxx_assert(output_length == expected_output_length);
+	      ++result.ptr;
+	      return result;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_precision(char* first, char* const 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;
+
+      // Extract the sign and exponent from the value.
+      const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+      // Ryu doesn't support formatting floating-point types larger than double
+      // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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 enough away from +-100.  But if it's
+	  // near +-100, then our log2 approximation is too coarse (and doesn't
+	  // consider precision-dependent rounding) in order to accurately
+	  // distinguish between a scientific exponent of +-100 and +-99.
+	  const bool scientific_exponent_near_100_p
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound = sign + strlen("d");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  if (scientific_exponent_near_100_p
+	      || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	    output_length_upper_bound += strlen("e+ddd");
+	  else
+	    output_length_upper_bound += strlen("e+dd");
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    first, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound
+			       || (scientific_exponent_near_100_p
+				   && (output_length
+				       == output_length_upper_bound - 1)));
+	    }
+	  else if (scientific_exponent_near_100_p)
+	    {
+	      // Write the result of d2exp_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    buffer, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound - 1
+			       || output_length == output_length_upper_bound);
+	      if (last - first < output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      memcpy(first, buffer, output_length);
+	    }
+	  else
+	    // If the scientific exponent is not near 100, then the upper bound
+	    // is actually the exact length, and so the result will definitely
+	    // not fit into the output range.
+	    return {last, errc::value_too_large};
+	  first += output_length;
+	  if (excess_precision > 0)
+	    {
+	      // Splice the excess zeros into 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;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      // Write the result of d2fixed_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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 form such as 1.00e-2 into the equivalent
+	  // fixed form 0.001.
+	  char buffer[4 + output_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  // Define the overloads for float.
+  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, float 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,
+	   int precision) noexcept
+  { return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+  // Define the overloads for double.
+  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, double 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); }
+
+  // Define the overloads for double.
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value),
+					  chars_format{});
+    else
+      return __floating_to_chars_shortest(first, last, value, chars_format{});
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value), fmt);
+    else
+      return __floating_to_chars_shortest(first, last, value, fmt);
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_precision(first, last, double(value), fmt,
+					   precision);
+    else
+      return __floating_to_chars_precision(first, last, value, fmt, precision);
+  }
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+  // Map the -mlong-double-64 long double overloads to the double overloads.
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_e(char* first, char* last, double value) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_d")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_format(char* first, char* last, double value,
+				     chars_format fmt) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_format")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
+				      chars_format fmt, int precision) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_formati")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..d995aa02d09
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-22  2:49 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-22  2:49 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:ffc3bdfd0d64916cd582509aae3a6b8b1ea6b63f

commit ffc3bdfd0d64916cd582509aae3a6b8b1ea6b63f
Author: Patrick Palka <ppalka@redhat.com>
Date:   Tue Jul 21 08:37:18 2020 -0400

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing explicit-precision
    fixed and scientific formatting of float and double. For
    explicit-precision formatting of long double we fall back to using
    printf.  Hexadecimal formatting for float, double and long double is
    implemented from scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the string a
    second time when the general formatting mode resolves to fixed.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's approach of being consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form of 1.08p+0 is 2.1p-1, but we output the former instead of the
       latter, as does printf.
    
    2. The Ryu routines for doing shortest formatting on types larger than
       binary64 use the __int128 type, and some targets (e.g. i386) have a
       large long double type but lack __int128.  For such targets we make
       the long double to_chars overloads go through the double overloads,
       which means we lose precision in the output.  (The mantissa of long
       double is 64 bits on i386, so I think we could potentially fix this
       by writing a specialized version of the generic Ryu formatting
       routine which works with uint64_t instead of __int128.)
    
    3. The __ibm128 shortest formatting routines don't guarantee
       round-trippability if the difference between the high- and low-order
       exponent is too large.  This is because we treat the type as if it
       has a contiguous 105-bit mantissa by merging the high- and low-order
       mantissas, so we potentially lose precision from the low-order part.
       Although this precision-dropping behavior is non-conforming, it seems
       consistent with how printf formats __ibm128.
    
    libstdc++-v3/ChangeLog:
    
            * config/abi/pre/gnu.ver: Add new exports.
            * include/std/charconv (to_chars): Declare the floating-point
            overloads for float, double and long 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.
            (to_chars): Define for float, double and long double.
            * testsuite/20_util/to_chars/long_double.cc: New test.

Diff:
---
 libstdc++-v3/config/abi/pre/gnu.ver                |    9 +
 libstdc++-v3/include/std/charconv                  |   24 +
 libstdc++-v3/src/c++17/Makefile.am                 |    1 +
 libstdc++-v3/src/c++17/Makefile.in                 |    3 +-
 libstdc++-v3/src/c++17/floating_to_chars.cc        | 1547 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 6 files changed, 1780 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 17aff5d907b..dbedd85068e 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2304,6 +2304,15 @@ GLIBCXX_3.4.29 {
     # std::from_chars
     _ZSt10from_charsPKcS0_R[def]St12chars_format;
 
+    # std::to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[fdeg];
+
+    # std::to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[fdeg]St12chars_format;
+
+    # std::to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[fdeg]St12chars_formati;
+
 } GLIBCXX_3.4.28;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index be668c1939e..b0f8a6106bc 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -702,6 +702,30 @@ namespace __detail
 	     chars_format __fmt = chars_format::general);
 #endif
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long 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 642efb976ac..0f5b7eb8e56 100644
--- a/libstdc++-v3/src/c++17/Makefile.am
+++ b/libstdc++-v3/src/c++17/Makefile.am
@@ -51,6 +51,7 @@ endif
 
 sources = \
 	floating_from_chars.cc \
+	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 ce08eb3ff11..bf291d7f236 100644
--- a/libstdc++-v3/src/c++17/Makefile.in
+++ b/libstdc++-v3/src/c++17/Makefile.in
@@ -124,7 +124,7 @@ 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 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
+am__objects_2 = floating_from_chars.lo 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 \
@@ -436,6 +436,7 @@ headers =
 
 sources = \
 	floating_from_chars.cc \
+	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..eb7cdf13e8b
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1547 @@
+// 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_ASSERTIONS
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We currently support the binary64, float80 (i.e. x86 80-bit extended
+// precision), binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#  define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_rep;
+      ieee_rep.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_rep.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_rep.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_rep;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (with an implicit leading bit).  We keep the exponent and sign
+      // of the high part, and merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_rep;
+      ieee_rep.mantissa = ((uint_t{mantissa_hi} << 64)
+			   | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_rep.biased_exponent = exponent_hi;
+      ieee_rep.sign = sign_hi;
+      return ieee_rep;
+    }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    typename floating_type_traits<T>::shortest_scientific_t
+    floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (std::is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (std::is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (std::is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						mantissa_bits, exponent_bits,
+						!has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  // 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.0 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
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+				   FP_ZERO, 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())
+	{
+	  if (!__builtin_signbit(value))
+	    str.remove_prefix(strlen("-"));
+
+	  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 = __builtin_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 of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_hex(char* first, char* last, const T value,
+			    optional<int> precision)
+    {
+      __glibcxx_requires_valid_range(first, last);
+
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr bool has_implicit_leading_bit
+	= floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+      constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+      if (precision && *precision < 0)
+	precision = nullopt;
+
+      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.
+      const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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;
+      static_assert(mantissa_t_width >= rounded_mantissa_bits);
+      mantissa_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								     - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = mantissa_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits;
+	  effective_mantissa <<= dropped_bits;
+	  if (should_round & (mantissa_t{1} << dropped_bits))
+	    {
+	      // Round up the least significant nibble.
+	      effective_mantissa += mantissa_t{1} << dropped_bits;
+	      // Check and adjust for overflow of the leading nibble.  When the
+	      // type has an implicit leading bit, then the leading nibble
+	      // before rounding is either 0 or 1, so it can't overflow.
+	      if constexpr (!has_implicit_leading_bit)
+		{
+		  // The only floating type with explicit leading mantissa bit
+		  // that we currently support is x86 80-bit extended precision.
+		  // So we hardcode the below overflow check for this type.
+		  static_assert(mantissa_t_width == 64
+				&& rounded_mantissa_bits == 64);
+		  if (effective_mantissa == 0)
+		    {
+		      // We rounded up the least significant nibble and the
+		      // mantissa overflowed, e.g f.fcp+10 with precision=1
+		      // became 10.0p+10.  Absorb the extra hexit into the
+		      // exponent to obtain 1.0p+14.
+		      effective_mantissa
+			= mantissa_t{1} << (rounded_mantissa_bits - 4);
+		      written_exponent += 4;
+		    }
+		}
+	    }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_written_exponent = abs(written_exponent);
+      expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_written_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_written_exponent >= 100 ? strlen("p+ddd")
+				 : abs_written_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      if (last - first < expected_output_length)
+	return {last, errc::value_too_large};
+
+      const auto saved_first = 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.
+	  int nibble_offset = rounded_mantissa_bits - 4;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+
+	       effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	       nibble_offset -= 4;
+	    }
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      const to_chars_result result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  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 = get_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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+NNNN")
+				     : abs_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, and so 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_rounded_up_pow10_p<T>(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
+	  // value_fits_inside_mantissa below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_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 = fd.sign + (result.ptr - first);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      return result;
+	    }
+	  else if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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 = 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 = result.ptr + 1 - orig_first;
+	      __glibcxx_assert(output_length == expected_output_length);
+	      ++result.ptr;
+	      return result;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_precision(char* first, char* const 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;
+
+      // Extract the sign and exponent from the value.
+      const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+      // Ryu doesn't support formatting floating-point types larger than double
+      // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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 enough away from +-100.  But if it's
+	  // near +-100, then our log2 approximation is too coarse (and doesn't
+	  // consider precision-dependent rounding) in order to accurately
+	  // distinguish between a scientific exponent of +-100 and +-99.
+	  const bool scientific_exponent_near_100_p
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound = sign + strlen("d");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  if (scientific_exponent_near_100_p
+	      || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	    output_length_upper_bound += strlen("e+ddd");
+	  else
+	    output_length_upper_bound += strlen("e+dd");
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    first, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound
+			       || (scientific_exponent_near_100_p
+				   && (output_length
+				       == output_length_upper_bound - 1)));
+	    }
+	  else if (scientific_exponent_near_100_p)
+	    {
+	      // Write the result of d2exp_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    buffer, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound - 1
+			       || output_length == output_length_upper_bound);
+	      if (last - first < output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      memcpy(first, buffer, output_length);
+	    }
+	  else
+	    // If the scientific exponent is not near 100, then the upper bound
+	    // is actually the exact length, and so the result will definitely
+	    // not fit into the output range.
+	    return {last, errc::value_too_large};
+	  first += output_length;
+	  if (excess_precision > 0)
+	    {
+	      // Splice the excess zeros into 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;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      // Write the result of d2fixed_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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 form such as 1.00e-2 into the equivalent
+	  // fixed form 0.001.
+	  char buffer[4 + output_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  // Define the overloads for float.
+  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, float 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,
+	   int precision) noexcept
+  { return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+  // Define the overloads for double.
+  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, double 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); }
+
+  // Define the overloads for double.
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value),
+					  chars_format{});
+    else
+      return __floating_to_chars_shortest(first, last, value, chars_format{});
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value), fmt);
+    else
+      return __floating_to_chars_shortest(first, last, value, fmt);
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_precision(first, last, double(value), fmt,
+					   precision);
+    else
+      return __floating_to_chars_precision(first, last, value, fmt, precision);
+  }
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+  // Map the -mlong-double-64 long double overloads to the double overloads.
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_e(char* first, char* last, double value) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_d")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_format(char* first, char* last, double value,
+				     chars_format fmt) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_format")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
+				      chars_format fmt, int precision) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_formati")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..d995aa02d09
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-20 14:38 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-20 14:38 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:7380f19a0ac5c3cbf23cd7a4fb31fb929c4beb85

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

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing explicit-precision
    fixed and scientific formatting of float and double. For
    explicit-precision formatting of long double we fall back to using
    printf.  Hexadecimal formatting for float, double and long double is
    implemented from scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the string a
    second time when the general formatting mode resolves to fixed.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's approach of being consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form of 1.08p+0 is 2.1p-1, but we output the former instead of the
       latter, as does printf.
    
    2. The Ryu routines for doing shortest formatting on types larger than
       binary64 use the __int128 type, and some targets (e.g. i386) have a
       large long double type but lack __int128.  For such targets we make
       the long double to_chars overloads go through the double overloads,
       which means we lose precision in the output.  (The mantissa of long
       double is 64 bits on i386, so I think we could potentially fix this
       by writing a specialized version of the generic Ryu formatting
       routine which works with uint64_t instead of __int128.)
    
    3. The __ibm128 shortest formatting routines don't guarantee
       round-trippability if the difference between the high- and low-order
       exponent is too large.  This is because we treat the type as if it
       has a contiguous 105-bit mantissa by merging the high- and low-order
       mantissas, so we potentially lose precision from the low-order part.
       Although this precision-dropping behavior is non-conforming, it seems
       consistent with how printf formats __ibm128.
    
    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 floating-point
            overloads for float, double and long 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/20_util/to_chars/long_double.cc: New test.
            * testsuite/util/testsuite_abi.cc: Add new symbol version.

Diff:
---
 libstdc++-v3/acinclude.m4                          |    2 +-
 libstdc++-v3/config/abi/pre/gnu.ver                |   12 +
 libstdc++-v3/configure                             |    2 +-
 libstdc++-v3/include/std/charconv                  |   24 +
 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        | 1547 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 libstdc++-v3/testsuite/util/testsuite_abi.cc       |    3 +-
 9 files changed, 1788 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..9a1bcfd25d1 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2299,6 +2299,18 @@ GLIBCXX_3.4.28 {
 
 } GLIBCXX_3.4.27;
 
+GLIBCXX_3.4.29 {
+    # to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[fdeg];
+
+    # to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[fdeg]St12chars_format;
+
+    # to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[fdeg]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..a1767a4ab2e 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -688,6 +688,30 @@ namespace __detail
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long 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..eb7cdf13e8b
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1547 @@
+// 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_ASSERTIONS
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We currently support the binary64, float80 (i.e. x86 80-bit extended
+// precision), binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#  define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_rep;
+      ieee_rep.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_rep.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_rep.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_rep;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (with an implicit leading bit).  We keep the exponent and sign
+      // of the high part, and merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_rep;
+      ieee_rep.mantissa = ((uint_t{mantissa_hi} << 64)
+			   | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_rep.biased_exponent = exponent_hi;
+      ieee_rep.sign = sign_hi;
+      return ieee_rep;
+    }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    typename floating_type_traits<T>::shortest_scientific_t
+    floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (std::is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (std::is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (std::is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						mantissa_bits, exponent_bits,
+						!has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  // 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.0 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
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+				   FP_ZERO, 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())
+	{
+	  if (!__builtin_signbit(value))
+	    str.remove_prefix(strlen("-"));
+
+	  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 = __builtin_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 of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_hex(char* first, char* last, const T value,
+			    optional<int> precision)
+    {
+      __glibcxx_requires_valid_range(first, last);
+
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr bool has_implicit_leading_bit
+	= floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+      constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+      if (precision && *precision < 0)
+	precision = nullopt;
+
+      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.
+      const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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;
+      static_assert(mantissa_t_width >= rounded_mantissa_bits);
+      mantissa_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								     - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = mantissa_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits;
+	  effective_mantissa <<= dropped_bits;
+	  if (should_round & (mantissa_t{1} << dropped_bits))
+	    {
+	      // Round up the least significant nibble.
+	      effective_mantissa += mantissa_t{1} << dropped_bits;
+	      // Check and adjust for overflow of the leading nibble.  When the
+	      // type has an implicit leading bit, then the leading nibble
+	      // before rounding is either 0 or 1, so it can't overflow.
+	      if constexpr (!has_implicit_leading_bit)
+		{
+		  // The only floating type with explicit leading mantissa bit
+		  // that we currently support is x86 80-bit extended precision.
+		  // So we hardcode the below overflow check for this type.
+		  static_assert(mantissa_t_width == 64
+				&& rounded_mantissa_bits == 64);
+		  if (effective_mantissa == 0)
+		    {
+		      // We rounded up the least significant nibble and the
+		      // mantissa overflowed, e.g f.fcp+10 with precision=1
+		      // became 10.0p+10.  Absorb the extra hexit into the
+		      // exponent to obtain 1.0p+14.
+		      effective_mantissa
+			= mantissa_t{1} << (rounded_mantissa_bits - 4);
+		      written_exponent += 4;
+		    }
+		}
+	    }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_written_exponent = abs(written_exponent);
+      expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_written_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_written_exponent >= 100 ? strlen("p+ddd")
+				 : abs_written_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      if (last - first < expected_output_length)
+	return {last, errc::value_too_large};
+
+      const auto saved_first = 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.
+	  int nibble_offset = rounded_mantissa_bits - 4;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+
+	       effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	       nibble_offset -= 4;
+	    }
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      const to_chars_result result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  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 = get_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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+NNNN")
+				     : abs_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, and so 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_rounded_up_pow10_p<T>(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
+	  // value_fits_inside_mantissa below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_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 = fd.sign + (result.ptr - first);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      return result;
+	    }
+	  else if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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 = 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 = result.ptr + 1 - orig_first;
+	      __glibcxx_assert(output_length == expected_output_length);
+	      ++result.ptr;
+	      return result;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_precision(char* first, char* const 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;
+
+      // Extract the sign and exponent from the value.
+      const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+      // Ryu doesn't support formatting floating-point types larger than double
+      // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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 enough away from +-100.  But if it's
+	  // near +-100, then our log2 approximation is too coarse (and doesn't
+	  // consider precision-dependent rounding) in order to accurately
+	  // distinguish between a scientific exponent of +-100 and +-99.
+	  const bool scientific_exponent_near_100_p
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound = sign + strlen("d");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  if (scientific_exponent_near_100_p
+	      || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	    output_length_upper_bound += strlen("e+ddd");
+	  else
+	    output_length_upper_bound += strlen("e+dd");
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    first, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound
+			       || (scientific_exponent_near_100_p
+				   && (output_length
+				       == output_length_upper_bound - 1)));
+	    }
+	  else if (scientific_exponent_near_100_p)
+	    {
+	      // Write the result of d2exp_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    buffer, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound - 1
+			       || output_length == output_length_upper_bound);
+	      if (last - first < output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      memcpy(first, buffer, output_length);
+	    }
+	  else
+	    // If the scientific exponent is not near 100, then the upper bound
+	    // is actually the exact length, and so the result will definitely
+	    // not fit into the output range.
+	    return {last, errc::value_too_large};
+	  first += output_length;
+	  if (excess_precision > 0)
+	    {
+	      // Splice the excess zeros into 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;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      // Write the result of d2fixed_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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 form such as 1.00e-2 into the equivalent
+	  // fixed form 0.001.
+	  char buffer[4 + output_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  // Define the overloads for float.
+  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, float 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,
+	   int precision) noexcept
+  { return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+  // Define the overloads for double.
+  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, double 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); }
+
+  // Define the overloads for double.
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value),
+					  chars_format{});
+    else
+      return __floating_to_chars_shortest(first, last, value, chars_format{});
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value), fmt);
+    else
+      return __floating_to_chars_shortest(first, last, value, fmt);
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_precision(first, last, double(value), fmt,
+					   precision);
+    else
+      return __floating_to_chars_precision(first, last, value, fmt, precision);
+  }
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+  // Map the -mlong-double-64 long double overloads to the double overloads.
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_e(char* first, char* last, double value) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_d")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_format(char* first, char* last, double value,
+				     chars_format fmt) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_format")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
+				      chars_format fmt, int precision) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_formati")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..d995aa02d09
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
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");


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-20 14:27 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-20 14:27 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:951c69c1562701b93cb6032dcf1d529b3e3b6ee8

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

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing explicit-precision
    fixed and scientific formatting of float and double. For
    explicit-precision formatting of long double we fall back to using
    printf.  Hexadecimal formatting for float, double and long double is
    implemented from scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the string a
    second time when the general formatting mode resolves to fixed.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's approach of being consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form of 1.08p+0 is 2.1p-1, but we output the former instead of the
       latter, as does printf.
    
    2. The Ryu routines for doing shortest formatting on types larger than
       binary64 use the __int128 type, and some targets (e.g. i386) have a
       large long double type but lack __int128.  For such targets we make
       the long double to_chars overloads go through the double overloads,
       which means we lose precision in the output.  (The mantissa of long
       double is 64 bits on i386, so I think we could potentially fix this
       by writing a specialized version of the generic Ryu formatting
       routine which works with uint64_t instead of __int128.)
    
    3. The __ibm128 shortest formatting routines don't guarantee
       round-trippability if the difference between the high- and low-order
       exponent is too large.  This is because we treat the type as if it
       has a contiguous 105-bit mantissa by merging the high- and low-order
       mantissas, so we potentially lose precision from the low-order part.
       Although this precision-dropping behavior is non-conforming, it seems
       consistent with how printf formats __ibm128.
    
    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 floating-point
            overloads for float, double and long 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/20_util/to_chars/long_double.cc: New test.
            * testsuite/util/testsuite_abi.cc: Add new symbol version.

Diff:
---
 libstdc++-v3/acinclude.m4                          |    2 +-
 libstdc++-v3/config/abi/pre/gnu.ver                |   12 +
 libstdc++-v3/configure                             |    2 +-
 libstdc++-v3/include/std/charconv                  |   24 +
 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        | 1546 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 libstdc++-v3/testsuite/util/testsuite_abi.cc       |    3 +-
 9 files changed, 1787 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..9a1bcfd25d1 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2299,6 +2299,18 @@ GLIBCXX_3.4.28 {
 
 } GLIBCXX_3.4.27;
 
+GLIBCXX_3.4.29 {
+    # to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[fdeg];
+
+    # to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[fdeg]St12chars_format;
+
+    # to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[fdeg]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..a1767a4ab2e 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -688,6 +688,30 @@ namespace __detail
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long 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..7b26fcc6afe
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1546 @@
+// 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_ASSERTIONS
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We currently support the binary64, float80 (i.e. x86 80-bit extended
+// precision), binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#  define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_rep;
+      ieee_rep.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_rep.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_rep.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_rep;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (with an implicit leading bit).  We keep the exponent and sign
+      // of the high part, and merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_rep;
+      ieee_rep.mantissa = ((uint_t{mantissa_hi} << 64)
+			   | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_rep.biased_exponent = exponent_hi;
+      ieee_rep.sign = sign_hi;
+      return ieee_rep;
+    }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    typename floating_type_traits<T>::shortest_scientific_t
+    floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (std::is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (std::is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (std::is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						mantissa_bits, exponent_bits,
+						!has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  // 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.0 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
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+				   FP_ZERO, 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())
+	{
+	  if (!__builtin_signbit(value))
+	    str.remove_prefix(strlen("-"));
+
+	  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 = __builtin_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 of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_hex(char* first, char* last, const T value,
+			    optional<int> precision)
+    {
+      __glibcxx_requires_valid_range(first, last);
+
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr bool has_implicit_leading_bit
+	= floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+      constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+      if (precision && *precision < 0)
+	precision = nullopt;
+
+      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.
+      const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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;
+      static_assert(mantissa_t_width >= rounded_mantissa_bits);
+      mantissa_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								     - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = mantissa_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits;
+	  effective_mantissa <<= dropped_bits;
+	  if (should_round & (mantissa_t{1} << dropped_bits))
+	    {
+	      // Round up the least significant nibble.
+	      effective_mantissa += mantissa_t{1} << dropped_bits;
+	      // Check and adjust for overflow of the leading nibble.  When the
+	      // type has an implicit leading bit, then the leading nibble
+	      // before rounding is either 0 or 1, so it can't overflow.
+	      if constexpr (!has_implicit_leading_bit)
+		{
+		  // The only floating type with explicit leading mantissa bit
+		  // that we currently support is x86 80-bit extended precision.
+		  // So we hardcode the below overflow check for this type.
+		  static_assert(mantissa_t_width == 64
+				&& rounded_mantissa_bits == 64);
+		  if (effective_mantissa == 0)
+		    {
+		      // We rounded up the least significant nibble and the
+		      // mantissa overflowed, e.g f.fcp+10 with precision=1
+		      // became 10.0p+10.  Absorb the extra hexit into the
+		      // exponent to obtain 1.0p+14.
+		      effective_mantissa
+			= mantissa_t{1} << (rounded_mantissa_bits - 4);
+		      written_exponent += 4;
+		    }
+		}
+	    }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_written_exponent = abs(written_exponent);
+      expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_written_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_written_exponent >= 100 ? strlen("p+ddd")
+				 : abs_written_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      if (last - first < expected_output_length)
+	return {last, errc::value_too_large};
+
+      const auto saved_first = 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.
+	  int nibble_offset = rounded_mantissa_bits - 4;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+
+	       effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	       nibble_offset -= 4;
+	    }
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      const to_chars_result result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  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 = get_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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+NNNN")
+				     : abs_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, and so 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_rounded_up_pow10_p<T>(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
+	  // value_fits_inside_mantissa below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_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 = fd.sign + (result.ptr - first);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      return result;
+	    }
+	  else if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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 = 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 = result.ptr + 1 - orig_first;
+	      __glibcxx_assert(output_length == expected_output_length);
+	      ++result.ptr;
+	      return result;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_precision(char* first, char* const 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;
+
+      // Extract the sign and exponent from the value.
+      const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+      // Ryu doesn't support formatting floating-point types larger than double
+      // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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 enough away from +-100.  But if it's
+	  // near +-100, then our log2 approximation is too coarse (and doesn't
+	  // consider precision-dependent rounding) in order to accurately
+	  // distinguish between a scientific exponent of +-100 and +-99.
+	  const bool scientific_exponent_near_100_p
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound = sign + strlen("d");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  if (scientific_exponent_near_100_p
+	      || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	    output_length_upper_bound += strlen("e+ddd");
+	  else
+	    output_length_upper_bound += strlen("e+dd");
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    first, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound
+			       || (scientific_exponent_near_100_p
+				   && (output_length
+				       == output_length_upper_bound - 1)));
+	    }
+	  else if (scientific_exponent_near_100_p)
+	    {
+	      // Write the result of d2exp_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    buffer, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound - 1
+			       || output_length == output_length_upper_bound);
+	      if (last - first < output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      memcpy(first, buffer, output_length);
+	    }
+	  else
+	    // If the scientific exponent is not near 100, then the upper bound
+	    // is actually the exact length, and so the result will definitely
+	    // not fit into the output range.
+	    return {last, errc::value_too_large};
+	  first += output_length;
+	  if (excess_precision > 0)
+	    {
+	      // Splice the excess zeros into 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;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      // Write the result of d2exp_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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 form such as 1.00e-2 into the equivalent
+	  // fixed form 0.001.
+	  char buffer[4 + output_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  // Define the overloads for float.
+  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, float 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,
+	   int precision) noexcept
+  { return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+  // Define the overloads for double.
+  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, double 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); }
+
+  // Define the overloads for double.
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value),
+					  chars_format{});
+    else
+      return __floating_to_chars_shortest(first, last, value, chars_format{});
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value), fmt);
+    else
+      return __floating_to_chars_shortest(first, last, value, fmt);
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_BINARY64
+		  || LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_precision(first, last, double(value), fmt,
+					   precision);
+    else
+      return __floating_to_chars_precision(first, last, value, fmt, precision);
+  }
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+  // Map the -mlong-double-64 long double overloads to the double overloads.
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_e(char* first, char* last, double value) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_d")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_format(char* first, char* last, double value,
+				     chars_format fmt) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_format")));
+
+  extern "C" to_chars_result
+  _ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
+				      chars_format fmt, int precision) noexcept
+    __attribute__((alias ("_ZSt8to_charsPcS_dSt12chars_formati")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..d995aa02d09
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
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");


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-18  1:43 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-18  1:43 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:1f6c6115f529b3fcae0c0123ca262a787268809e

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

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing explicit-precision
    fixed and scientific formatting of float and double. For
    explicit-precision formatting of long double we fall back to using
    printf.  Hexadecimal formatting for float, double and long double is
    implemented from scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the string a
    second time when the general formatting mode resolves to fixed.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's approach of being consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form of 1.08p+0 is 2.1p-1, but we output the former instead of the
       latter, as does printf.
    
    2. The Ryu routines for doing shortest formatting on types larger than
       binary64 use the __int128 type, and some targets (e.g. i386) have a
       large long double type but lack __int128.  For such targets we make
       the long double to_chars overloads go through the double overloads,
       which means we lose precision in the output.  (The mantissa of long
       double is 64 bits on i386, so I think we could potentially fix this
       by writing a specialized version of the generic Ryu formatting
       routine which works with uint64_t instead of __int128.)
    
    3. The __ibm128 shortest formatting routines don't guarantee
       round-trippability if the difference between the high- and low-order
       exponent is too large.  This is because we treat the type as if it
       has a contiguous 105-bit mantissa by merging the high- and low-order
       mantissas, so we potentially lose precision from the low-order part.
       Although this precision-dropping behavior is non-conforming, it seems
       consistent with how printf formats __ibm128.
    
    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 floating-point
            overloads for float, double and long 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/20_util/to_chars/long_double.cc: New test.
            * testsuite/util/testsuite_abi.cc: Add new symbol version.

Diff:
---
 libstdc++-v3/acinclude.m4                          |    2 +-
 libstdc++-v3/config/abi/pre/gnu.ver                |   12 +
 libstdc++-v3/configure                             |    2 +-
 libstdc++-v3/include/std/charconv                  |   42 +
 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        | 1530 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 libstdc++-v3/testsuite/util/testsuite_abi.cc       |    3 +-
 9 files changed, 1789 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..9a1bcfd25d1 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2299,6 +2299,18 @@ GLIBCXX_3.4.28 {
 
 } GLIBCXX_3.4.27;
 
+GLIBCXX_3.4.29 {
+    # to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[fdeg];
+
+    # to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[fdeg]St12chars_format;
+
+    # to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[fdeg]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..e5963ec6cf2 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -688,6 +688,48 @@ namespace __detail
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
+  // Floating-point std::to_chars
+
+  // Overloads for float.
+  to_chars_result to_chars(char* __first, char* __last, float __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, float __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for double.
+  to_chars_result to_chars(char* __first, char* __last, double __value) 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, double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // Overloads for long double.
+  to_chars_result to_chars(char* __first, char* __last, long double __value)
+    noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt) noexcept;
+  to_chars_result to_chars(char* __first, char* __last, long double __value,
+			   chars_format __fmt, int __precision) noexcept;
+
+  // If long double is equivalent to  double, then we just define the long
+  // double overloads as wrappers around the corresponding double overloads.
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+  inline to_chars_result
+  to_chars(char* __first, char* __last, long double __value) noexcept
+  { return to_chars(__first, __last, double(__value)); }
+
+  inline to_chars_result
+  to_chars(char* __first, char* __last, long double __value,
+	   chars_format __fmt) noexcept
+  { return to_chars(__first, __last, double(__value), __fmt); }
+
+  inline to_chars_result
+  to_chars(char* __first, char* __last, long double __value,
+	   chars_format __fmt, int __precision) noexcept
+  { return to_chars(__first, __last, double(__value), __fmt, __precision); }
+#endif
+
 _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..3a3cfcf9c13
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1530 @@
+// 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_ASSERTIONS
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We currently support the binary64, float80 (i.e. x86 80-bit extended
+// precision), binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#   define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu bits in their own namespace to avoid name conflicts.
+# include "ryu/generic_128.h"
+# include "ryu/ryu_generic_128.h"
+# include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_rep;
+      ieee_rep.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_rep.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_rep.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_rep;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (with an implicit leading bit).  We keep the exponent and sign
+      // of the high part, and merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_rep;
+      ieee_rep.mantissa = ((uint_t{mantissa_hi} << 64)
+			   | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_rep.biased_exponent = exponent_hi;
+      ieee_rep.sign = sign_hi;
+      return ieee_rep;
+    }
+#endif
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    typename floating_type_traits<T>::shortest_scientific_t
+    floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (std::is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (std::is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (std::is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						mantissa_bits, exponent_bits,
+						!has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  // 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.0 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
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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 (__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL,
+				   FP_ZERO, 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())
+	{
+	  if (!__builtin_signbit(value))
+	    str.remove_prefix(strlen("-"));
+
+	  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 = __builtin_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 of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_hex(char* first, char* last, const T value,
+			    optional<int> precision)
+    {
+      __glibcxx_requires_valid_range(first, last);
+
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr bool has_implicit_leading_bit
+	= floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+      constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+      if (precision && *precision < 0)
+	precision = nullopt;
+
+      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.
+      const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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;
+      static_assert(mantissa_t_width >= rounded_mantissa_bits);
+      mantissa_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								     - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = mantissa_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits;
+	  effective_mantissa <<= dropped_bits;
+	  if (should_round & (mantissa_t{1} << dropped_bits))
+	    {
+	      // Round up the least significant nibble.
+	      effective_mantissa += mantissa_t{1} << dropped_bits;
+	      // Check and adjust for overflow of the leading nibble.  When the
+	      // type has an implicit leading bit, then the leading nibble
+	      // before rounding is either 0 or 1, so it can't overflow.
+	      if constexpr (!has_implicit_leading_bit)
+		{
+		  // The only floating type with explicit leading mantissa bit
+		  // that we currently support is x86 80-bit extended precision.
+		  // So we hardcode the below overflow check for this type.
+		  static_assert(mantissa_t_width == 64
+				&& rounded_mantissa_bits == 64);
+		  if (effective_mantissa == 0)
+		    {
+		      // We rounded up the least significant nibble and the
+		      // mantissa overflowed, e.g f.fcp+10 with precision=1
+		      // became 10.0p+10.  Absorb the extra hexit into the
+		      // exponent to obtain 1.0p+14.
+		      effective_mantissa
+			= mantissa_t{1} << (rounded_mantissa_bits - 4);
+		      written_exponent += 4;
+		    }
+		}
+	    }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_written_exponent = abs(written_exponent);
+      expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_written_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_written_exponent >= 100 ? strlen("p+ddd")
+				 : abs_written_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      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.
+	  int nibble_offset = rounded_mantissa_bits - 4;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+
+	       effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	       nibble_offset -= 4;
+	    }
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      const to_chars_result result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  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 = get_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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+NNNN")
+				     : abs_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, and so 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_rounded_up_pow10_p<T>(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
+	  // value_fits_inside_mantissa below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_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 if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_precision(char* first, char* const 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;
+
+      // Extract the sign and exponent from the value.
+      const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+      // Ryu doesn't support formatting floating-point types larger than double
+      // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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 enough away from +-100.  But if it's
+	  // near +-100, then our log2 approximation is too coarse (and doesn't
+	  // consider precision-dependent rounding) in order to accurately
+	  // distinguish between a scientific exponent of +-100 and +-99.
+	  const bool scientific_exponent_near_100_p
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound = sign + strlen("d");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  if (scientific_exponent_near_100_p
+	      || (floor_log2_value >= 332 || floor_log2_value <= -333))
+	    output_length_upper_bound += strlen("e+ddd");
+	  else
+	    output_length_upper_bound += strlen("e+dd");
+
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    first, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound
+			       || (scientific_exponent_near_100_p
+				   && (output_length
+				       == output_length_upper_bound - 1)));
+	    }
+	  else if (scientific_exponent_near_100_p)
+	    {
+	      // Write the result of d2exp_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    buffer, nullptr);
+	      __glibcxx_assert(output_length == output_length_upper_bound - 1
+			       || output_length == output_length_upper_bound);
+	      if (last - first < output_length + excess_precision)
+		return {last, errc::value_too_large};
+	      memcpy(first, buffer, output_length);
+	    }
+	  else
+	    // If the scientific exponent is not near 100, then the upper bound
+	    // is actually the exact length, and so the result will definitely
+	    // not fit into the output range.
+	    return {last, errc::value_too_large};
+	  first += output_length;
+	  if (excess_precision > 0)
+	    {
+	      // Splice the excess zeros into 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;
+
+	  // Compute an upper bound on the output length.  TODO: Maybe also
+	  // consider a lower bound on the output length.
+	  int output_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      // The result will definitely fit into the output range, so we can
+	      // write directly into it.
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      // Write the result of d2exp_buffered_n into an intermediate
+	      // buffer, do a bounds check, and copy the result into the output
+	      // range.
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  // Define the overloads for float.
+  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, float 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,
+	   int precision) noexcept
+  { return __floating_to_chars_precision(first, last, value, fmt, precision); }
+
+  // Define the overloads for double.
+  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, double 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); }
+
+  // Define the overloads for double, if they haven't already been defined
+  // inline in <charconv>.
+#if LONG_DOUBLE_KIND != LDK_BINARY64
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value),
+					  chars_format{});
+    else
+      return __floating_to_chars_shortest(first, last, value, chars_format{});
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_shortest(first, last, double(value), fmt);
+    else
+      return __floating_to_chars_shortest(first, last, value, fmt);
+  }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+    if constexpr (LONG_DOUBLE_KIND == LDK_UNSUPPORTED)
+      return __floating_to_chars_precision(first, last, double(value), fmt,
+					   precision);
+    else
+      return __floating_to_chars_precision(first, last, value, fmt, precision);
+  }
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..d995aa02d09
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
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");


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-17  4:27 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-17  4:27 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:dcafe436c8a315f34b607d7d928cb8eb6f689933

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

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing explicit-precision
    fixed and scientific formatting of float and double. For
    explicit-precision formatting of long double we fall back to using
    printf.  Hexadecimal formatting for float, double and long double is
    implemented from scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length
    beforehand, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer if
    necessary.
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu,
    and where we also take care to avoid having to format the string a
    second time when the general formatting mode resolves to fixed.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's approach of being consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form of 1.08p+0 is 2.1p-1, but we output the former instead of the
       latter, as does printf.
    
    2. The Ryu routines for doing shortest formatting on types larger than
       binary64 use the __int128 type, and some targets (e.g. i386) have a
       large long double type but lack __int128.  For these targets we make
       the long double to_chars overloads go through the double overloads,
       which means we lose precision in the output.  (The mantissa of long
       double is 64 bits on i386, so I think we could potentially fix this
       by writing a specialized version of the generic Ryu formatting
       routine which works with uint64_t instead of __int128.)
    
    3. The __ibm128 shortest formatting routines don't guarantee
       round-trippability if the difference between the high- and low-order
       exponent is too large.  This is because we treat the type as if it
       has a contiguous 105-bit mantissa by merging the high- and low-order
       mantissas, so we potentially lose precision from the low-order part.
       Although this precision-dropping behavior is non-conforming, it seems
       consistent with how printf formats __ibm128.
    
    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 floating-point
            overloads for float, double and long 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/20_util/to_chars/long_double.cc: New test.
            * testsuite/util/testsuite_abi.cc: Add new symbol version.

Diff:
---
 libstdc++-v3/acinclude.m4                          |    2 +-
 libstdc++-v3/config/abi/pre/gnu.ver                |   12 +
 libstdc++-v3/configure                             |    2 +-
 libstdc++-v3/include/std/charconv                  |   21 +
 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        | 1514 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 libstdc++-v3/testsuite/util/testsuite_abi.cc       |    3 +-
 9 files changed, 1752 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..9a1bcfd25d1 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2299,6 +2299,18 @@ GLIBCXX_3.4.28 {
 
 } GLIBCXX_3.4.27;
 
+GLIBCXX_3.4.29 {
+    # to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[fdeg];
+
+    # to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[fdeg]St12chars_format;
+
+    # to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[fdeg]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..042cc4fbcca 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -688,6 +688,27 @@ namespace __detail
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
+  // Floating-point std::to_chars
+
+  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, long 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, long 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;
+  to_chars_result to_chars(char* __first, char* __last, long 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..64e4a378fc2
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1514 @@
+// 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_ASSERTIONS
+
+#include <charconv>
+
+#include <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We currently support the binary64, float80 (i.e. x86 80-bit extended
+// precision), binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#   define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu stuff in its own namespace to avoid name conflicts.
+#include "ryu/generic_128.h"
+#include "ryu/ryu_generic_128.h"
+#include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_rep;
+      ieee_rep.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_rep.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_rep.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_rep;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (with an implicit leading bit).  We keep the exponent and sign
+      // of the high part, and merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_rep;
+      ieee_rep.mantissa = ((uint_t{mantissa_hi} << 64)
+			   | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_rep.biased_exponent = exponent_hi;
+      ieee_rep.sign = sign_hi;
+      return ieee_rep;
+    }
+#endif
+
+  // 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.0 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
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(const ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(const ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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())
+	{
+	  if (!signbit(value))
+	    str.remove_prefix(strlen("-"));
+
+	  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 of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_hex(char* first, char* last, const T value,
+			    optional<int> precision)
+    {
+      __glibcxx_requires_valid_range(first, last);
+
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr bool has_implicit_leading_bit
+	= floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+      constexpr int mantissa_t_width = sizeof(mantissa_t) * __CHAR_BIT__;
+
+      if (precision && *precision < 0)
+	precision = nullopt;
+
+      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.
+      const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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;
+      static_assert(mantissa_t_width >= rounded_mantissa_bits);
+      mantissa_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								     - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = mantissa_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits;
+	  effective_mantissa <<= dropped_bits;
+	  if (should_round & (mantissa_t{1} << dropped_bits))
+	    {
+	      // Round up the least significant nibble.
+	      effective_mantissa += mantissa_t{1} << dropped_bits;
+	      // Check and adjust for overflow of the leading nibble.  When the
+	      // type has an implicit leading bit, then the leading nibble
+	      // before rounding is either 0 or 1, so it can't overflow.
+	      if constexpr (!has_implicit_leading_bit)
+		{
+		  // The only floating type with explicit leading mantissa bit
+		  // that we currently support is x86 80-bit extended precision.
+		  // So we hardcode the below overflow check for this type.
+		  static_assert(mantissa_t_width == 64
+				&& rounded_mantissa_bits == 64);
+		  if (effective_mantissa == 0)
+		    {
+		      // We rounded up the least significant nibble and the
+		      // mantissa overflowed, e.g f.fcp+10 with precision=1
+		      // became 10.0p+10.  Absorb the extra hexit into the
+		      // exponent to obtain 1.0p+14.
+		      effective_mantissa
+			= mantissa_t{1} << (rounded_mantissa_bits - 4);
+		      written_exponent += 4;
+		    }
+		}
+	    }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_written_exponent = abs(written_exponent);
+      expected_output_length += (abs_written_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_written_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_written_exponent >= 100 ? strlen("p+ddd")
+				 : abs_written_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      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.
+	  int nibble_offset = rounded_mantissa_bits - 4;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+
+	       effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	       nibble_offset -= 4;
+	    }
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      const to_chars_result result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point number.
+  template<typename T>
+    static typename floating_type_traits<T>::shortest_scientific_t
+    __floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						  mantissa_bits, exponent_bits,
+						  !has_implicit_leading_bit);
+	}
+#endif
+    }
+
+  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 = get_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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+NNNN")
+				     : abs_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, and so 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_rounded_up_pow10_p<T>(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
+	  // value_fits_inside_mantissa below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_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 if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  template<typename T>
+    static to_chars_result
+    __floating_to_chars_precision(char* first, char* const 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;
+
+      // Extract the sign and exponent from the value.
+      const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+    // Ryu doesn't support formatting floating-point types larger than double
+    // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward obvious upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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.  Otherwise, our
+	  // floor(log2) is too coarse-grained and doesn't consider
+	  // precision-dependent rounding in order to distinguish between a
+	  // scientific exponent of +-100 or +-99.
+	  // We handle this edge case by first writing the result to a
+	  // sufficiently sized output buffer.
+	  const bool scientific_exponent_near_100
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+	  int output_length;
+	  if (scientific_exponent_near_100) [[unlikely]]
+	    {
+	      const int output_length_upper_bound
+		= strlen("-d.") + effective_precision + strlen("e+ddd");
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    buffer, nullptr);
+	      __glibcxx_assert(output_length <= output_length_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.
+	      int expected_output_length = sign + strlen("d");
+	      if (effective_precision > 0)
+		expected_output_length += strlen(".") + effective_precision;
+	      expected_output_length
+		+= ((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_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  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) noexcept
+  { return __floating_to_chars_shortest(first, last, value, chars_format{}); }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+#if LONG_DOUBLE_KIND == LDK_BINARY64 || LONG_DOUBLE_KIND == LDK_UNSUPPORTED
+    return __floating_to_chars_shortest(first, last,
+					static_cast<double>(value),
+					chars_format{});
+#else
+    return __floating_to_chars_shortest(first, last, value, chars_format{});
+#endif
+  }
+
+  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) noexcept
+  { return __floating_to_chars_shortest(first, last, value, fmt); }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+#if LONG_DOUBLE_KIND == LDK_BINARY64 || LONG_DOUBLE_KIND == LDK_UNSUPPORTED
+    return __floating_to_chars_shortest(first, last,
+					static_cast<double>(value),
+					fmt);
+#else
+    return __floating_to_chars_shortest(first, last, value, fmt);
+#endif
+  }
+
+  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); }
+
+  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, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+#if LONG_DOUBLE_KIND == LDK_BINARY64 || LONG_DOUBLE_KIND == LDK_UNSUPPORTED
+    return __floating_to_chars_precision(first, last,
+					 static_cast<double>(value),
+					 fmt, precision);
+#else
+    return __floating_to_chars_precision(first, last, value, fmt, precision);
+#endif
+  }
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..d995aa02d09
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
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");


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-15 19:01 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-15 19:01 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:46f7b2daed1572a76a3e5cd22ac26b4fc10b103a

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

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing fixed and scientific
    formatting of float and double. For formatting long double with an
    explicit precision argument we use a printf fallback.  Hexadecimal
    formatting for float, double and long double is implemented from
    scratch.
    
    The supported long double binary formats are binary64, binary80 (x86
    80-bit extended precision), binary128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length before
    the fact, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer (if the
    output range is smaller than the upper bound).
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu, and
    where we also take care to avoid having to format the string a second
    time when the general formatting mode resolves to fixed.
    
    This implementation is non-conforming in a couple of ways:
    
    1. For the shortest hexadecimal formatting, we currently follow the
       Microsoft implementation's approach of being consistent with the
       output of printf's '%a' specifier at the expense of sometimes not
       printing the shortest representation.  For example, the shortest hex
       form of 1.08p+0 is 2.1p-1, but we output the former instead of the
       latter, as does printf.
    
    2. The Ryu routines for doing shortest formatting on types larger than
       binary64 use the __int128 type, and some targets (e.g.  i386) have a
       large long double type but lack __int128.  For such targets we make
       the long double to_chars overloads go through the double overloads,
       which means we lose precision in the output.  (The mantissa of long
       double is 64 bits on i386, so I think we could potentially fix this
       writing a specialized version of the generic Ryu formatting
       routine which works with uint64_t instead of __int128.)
    
    3. The __ibm128 shortest formatting routines don't guarantee
       round-trippability if the difference between the low-order and
       high-order exponent is too large.  This is because we treat the type
       as if it has a contiguous 105-bit mantissa by merging the high-order
       and low-order mantissas, so we potentially lose precision from the
       low-order part.  Although this precision-dropping behavior is
       non-conforming, it seems consistent with how printf formats __ibm128.
    
    Tested so far on x86_64-pc-linux-gnu and on powerpc64-unknown-linux-gnu.
    
    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 floating-point
            overloads for float, double and long 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/20_util/to_chars/long_double.cc: New test.
            * testsuite/util/testsuite_abi.cc: Add new symbol version.

Diff:
---
 libstdc++-v3/acinclude.m4                          |    2 +-
 libstdc++-v3/config/abi/pre/gnu.ver                |   12 +
 libstdc++-v3/configure                             |    2 +-
 libstdc++-v3/include/std/charconv                  |   21 +
 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        | 1515 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 libstdc++-v3/testsuite/util/testsuite_abi.cc       |    3 +-
 9 files changed, 1753 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..9a1bcfd25d1 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2299,6 +2299,18 @@ GLIBCXX_3.4.28 {
 
 } GLIBCXX_3.4.27;
 
+GLIBCXX_3.4.29 {
+    # to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[fdeg];
+
+    # to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[fdeg]St12chars_format;
+
+    # to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[fdeg]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..042cc4fbcca 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -688,6 +688,27 @@ namespace __detail
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
+  // Floating-point std::to_chars
+
+  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, long 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, long 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;
+  to_chars_result to_chars(char* __first, char* __last, long 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..fd83537110f
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1515 @@
+// 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 <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+
+// Determine the binary format of 'long double'.
+
+// We currently support the binary64, float80 (i.e. x86 80-bit extended
+// precision), binary128, and ibm128 formats.
+#define LDK_UNSUPPORTED 0
+#define LDK_BINARY64    1
+#define LDK_FLOAT80     2
+#define LDK_BINARY128   3
+#define LDK_IBM128      4
+
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+# define LONG_DOUBLE_KIND LDK_BINARY64
+#elif defined(__SIZEOF_INT128__)
+// The Ryu routines need a 128-bit integer type in order to do shortest
+// formatting of types larger than 64-bit double, so without __int128 we can't
+// support any large long double format.  This is the case for e.g. i386.
+# if __LDBL_MANT_DIG__ == 64
+#   define LONG_DOUBLE_KIND LDK_FLOAT80
+# elif __LDBL_MANT_DIG__ == 113
+#  define LONG_DOUBLE_KIND LDK_BINARY128
+# elif __LDBL_MANT_DIG__ == 106
+#  define LONG_DOUBLE_KIND LDK_IBM128
+# endif
+#endif
+#if !defined(LONG_DOUBLE_KIND)
+# define LONG_DOUBLE_KIND LDK_UNSUPPORTED
+#endif
+
+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"
+
+#ifdef __SIZEOF_INT128__
+    namespace generic128
+    {
+      // Put the generic Ryu stuff in its own namespace to avoid name conflicts.
+#include "ryu/generic_128.h"
+#include "ryu/ryu_generic_128.h"
+#include "ryu/generic_128.c"
+    } // namespace generic128
+
+    using generic128::floating_decimal_128;
+    using generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return generic128::generic_to_chars(v, result); }
+#endif
+  } // namespace ryu
+
+  // A traits class that contains pertinent information about the binary
+  // format of each of the floating-point types we support.
+  template<typename T>
+    struct floating_type_traits
+    { };
+
+  template<>
+    struct floating_type_traits<float>
+    {
+      // We (and Ryu) assume float has the IEEE binary32 format.
+      static_assert(__FLT_MANT_DIG__ == 24);
+      static constexpr int mantissa_bits = 23;
+      static constexpr int exponent_bits = 8;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint32_t;
+      using shortest_scientific_t = ryu::floating_decimal_32;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000011101011100110101100101101110000000000000000000000000 };
+    };
+
+  template<>
+    struct floating_type_traits<double>
+    {
+      // We (and Ryu) assume double has the IEEE binary64 format.
+      static_assert(__DBL_MANT_DIG__ == 53);
+      static constexpr int mantissa_bits = 52;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_64;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	    0b0111100011110101011000011110000000110110010101011000001110011111,
+	    0b0101101100000000011100100100111100110110110100010001010101110000,
+	    0b0011110010111000101111110101100011101100010001010000000101100111,
+	    0b0001010000011001011100100001010000010101101000001101000000000000 };
+    };
+
+#if LONG_DOUBLE_KIND == LDK_FLOAT80
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 64;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = false;
+      using mantissa_t = uint64_t;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	    0b1001100101001111010011011111101000101111110001011001011101110000,
+	    0b0000101111111011110010001000001010111101011110111111010100011001,
+	    0b0011100000011111001101101011111001111100100010000101001111101001,
+	    0b0100100100000000100111010010101110011000110001101101110011001010,
+	    0b0111100111100010100000010011000010010110101111110101000011110100,
+	    0b1010100111100010011110000011011101101100010110000110101010101010,
+	    0b0000001111001111000000101100111011011000101000110011101100110010,
+	    0b0111000011100100101101010100001101111110101111001000010011111111,
+	    0b0010111000100110100100100010101100111010110001101010010111001000,
+	    0b0000100000010110000011001001000111000001111010100101101000001111,
+	    0b0010101011101000111100001011000010011101000101010010010000101111,
+	    0b1011111011101101110010101011010001111000101000101101011001100011,
+	    0b1010111011011011110111110011001010000010011001110100101101000101,
+	    0b0011000001110110011010010000011100100011001011001100001101010110,
+	    0b0100011111011000111111101000011110000010111110101001000000001001,
+	    0b1110000001110001001101101110011000100000001010000111100010111010,
+	    0b1110001001010011101000111000001000010100110000010110100011110000,
+	    0b0000011010110000110001111000011111000011001101001101001001000110,
+	    0b1010010111001000101001100101010110100100100010010010000101000010,
+	    0b1011001110000111100010100110000011100011111001110111001100000101,
+	    0b0110101001001000010110001000010001010101110101100001111100011001,
+	    0b1111100011110101011110011010101001010010100011000010110001101001,
+	    0b0100000100001000111101011100010011011111011001000000001100011000,
+	    0b1110111111000111100101110111110000000011001110011100011011011001,
+	    0b1100001100100000010001100011011000111011110000110011010101000011,
+	    0b1111111011100111011101001111111000010000001111010111110010000100,
+	    0b1110111001111110101111000101000000001010001110011010001000111010,
+	    0b1000010001011000101111111010110011111101110101101001111000111010,
+	    0b0100000111101001000111011001101000001010111011101001101111000100,
+	    0b0000011100110001000111011100111100110001101111111010110111100000,
+	    0b0000011101011100100110010011110101010100010011110010010111010000,
+	    0b0011011001100111110101111100001001101110101101001110110011110110,
+	    0b1011000101000001110100111001100100111100110011110000000001101000,
+	    0b1011100011110100001001110101010110111001000000001011101001011110,
+	    0b1111001010010010100000010110101010101011101000101000000000001100,
+	    0b1000001111100100111001110101100001010011111111000001000011110000,
+	    0b0001011101001000010000101101111000001110101100110011001100110111,
+	    0b1110011100000010101011011111001010111101111110100000011100000011,
+	    0b1001110110011100101010011110100010110001001110110000101011100110,
+	    0b1001101000100011100111010000011011100001000000110101100100001001,
+	    0b1010111000101000101101010111000010001100001010100011111100000100,
+	    0b0111101000100011000101101011111011100010001101110111001111001011,
+	    0b1110100111010110001110110110000000010110100011110000010001111100,
+	    0b1100010100011010001011001000111001010101011110100101011001000000,
+	    0b0000110001111001100110010110111010101101001101000000000010010101,
+	    0b0001110111101000001111101010110010010000111110111100000111110100,
+	    0b0111110111001001111000110001101101001010101110110101111110000100,
+	    0b0000111110111010101111100010111010011100010110011011011001000001,
+	    0b1010010100100100101110111111111000101100000010111111101101000110,
+	    0b1000100111111101100011001101000110001000000100010101010100001101,
+	    0b1100101010101000111100101100001000110001110010100000000010110101,
+	    0b1010000100111101100100101010010110100010000000110101101110000100,
+	    0b1011111011110001110000100100000000001010111010001101100000100100,
+	    0b0111101101100011001110011100000001000101101101111000100111011111,
+	    0b0100111010010011011001010011110100001100111010010101111111100011,
+	    0b0010001001011000111000001100110111110111110010100011000110110110,
+	    0b0101010110000000010000100000110100111011111101000100000111010010,
+	    0b0110000011011101000001010100110101101110011100110101000000001001,
+	    0b1101100110100000011000001111000100100100110001100110101010101100,
+	    0b0010100101010110010010001010101000011111111111001011001010001111,
+	    0b0111001010001111001100111001010101001000110101000011110000001000,
+	    0b0110010011001001001111110001010010001011010010001101110110110011,
+	    0b0110010100111011000100111000001001101011111001110010111110111111,
+	    0b0101110111001001101100110100101001110010101110011001101110001000,
+	    0b0100110101010111011010001100010111100011010011111001010100111000,
+	    0b0111000110110111011110100100010111000110000110110110110001111110,
+	    0b1000101101010100100100111110100011110110110010011001110011110101,
+	    0b1001101110101001010100111101101011000101000010110101101111110000,
+	    0b0100100101001011011001001011000010001101001010010001010110101000,
+	    0b0010100001001011100110101000010110000111000111000011100101011011,
+	    0b0110111000011001111101101011111010001000000010101000101010011110,
+	    0b1000110110100001111011000001111100001001000000010110010100100100,
+	    0b1001110100011111100111101011010000010101011100101000010010100110,
+	    0b0001010110101110100010101010001110110110100011101010001001111100,
+	    0b1010100101101100000010110011100110100010010000100100001110000100,
+	    0b0001000000010000001010000010100110000001110100111001110111101101,
+	    0b1100000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_BINARY128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 112;
+      static constexpr int exponent_bits = 15;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	    0b1011001111110100000100010101101110011100100110000110010110011000,
+	    0b1010100010001101111111000000001101010010100010010000111011110111,
+	    0b1011111001110001111000011111000010110111000111110100101010100101,
+	    0b0110100110011110011011000011000010011001110001001001010011100011,
+	    0b0000011111110010101111101011101010000110011111100111001110100111,
+	    0b0100010101010110000010111011110100000010011001001010001110111101,
+	    0b1101110111000010001101100000110100000111001001101011000101011011,
+	    0b0100111011101101010000001101011000101100101110010010110000101011,
+	    0b0100000110111000000110101000010011101000110100010110000011101101,
+	    0b1011001101001000100001010001100100001111011101010101110001010110,
+	    0b1000000001000000101001110010110010001111101101010101001100000110,
+	    0b0101110110100110000110000001001010111110001110010000111111010011,
+	    0b1010001111100111000100011100100100111100100101000001011001000111,
+	    0b1010011000011100110101100111001011100101111111100001110100000100,
+	    0b1100011100100010100000110001001010000000100000001001010111011101,
+	    0b0101110000100011001111101101000000100110000010010111010001111010,
+	    0b0100111100011010110111101000100110000111001001101100000001111100,
+	    0b1100100100111110101011000100000101011010110111000111110100110101,
+	    0b0110010000010111010100110011000000111010000010111011010110000100,
+	    0b0101001001010010110111010111000101011100000111100111000001110010,
+	    0b1101111111001011101010110001000111011010111101001011010110100100,
+	    0b0001000100110000011111101011001101110010110110010000000011100100,
+	    0b0001000000000101001001001000000000011000100011001110101001001110,
+	    0b0010010010001000111010011011100001000110011011011110110100111000,
+	    0b0000100110101100000111100010100100011100110111011100001111001100,
+	    0b1011111010001110001100000011110111111111100000001011111111101100,
+	    0b0000011100001111010101110000100110111100101101110111101001000001,
+	    0b1100010001110110111100001001001101101000011100000010110101001011,
+	    0b0100101001101011111001011110101101100011011111011100101010101111,
+	    0b0001101001111001110000101101101100001011010001011110011101000010,
+	    0b1111000000101001101111011010110011101110100001011011001011100010,
+	    0b0101001010111101101100001111100010010110001101001000001101100100,
+	    0b0101100101011110001100101011111000111001111001001001101101100001,
+	    0b1111001101010010100100011011000110110010001111000111010001001101,
+	    0b0001110010011000000001000110110111011000011100001000011001110111,
+	    0b0100001011011011011011110011101100100101111111101100101000001110,
+	    0b0101011110111101010111100111101111000101111111111110100011011010,
+	    0b1110101010001001110100000010110111010111111010111110100110010110,
+	    0b1010001111100001001100101000110100001100011100110010000011010111,
+	    0b1111111101101111000100111100000101011000001110011011101010111001,
+	    0b1111101100001110100101111101011001000100000101110000110010100011,
+	    0b1001010110110101101101000101010001010000101011011111010011010000,
+	    0b0111001110110011101001100111000001000100001010110000010000001101,
+	    0b0101111100111110100111011001111001111011011110010111010011101010,
+	    0b1110111000000001100100111001100100110001011011001110101111110111,
+	    0b0001010001001101010111101010011111000011110001101101011001111111,
+	    0b0101000011100011010010001101100001011101011010100110101100100010,
+	    0b0001000101011000100101111100110110000101101101111000110001001011,
+	    0b0101100101001011011000010101000000010100011100101101000010011111,
+	    0b1000010010001011101001011010100010111011110100110011011000100111,
+	    0b1000011011100001010111010111010011101100100010010010100100101001,
+	    0b1001001001010111110101000010111010000000101111010100001010010010,
+	    0b0011011110110010010101111011000001000000000011011111000011111011,
+	    0b1011000110100011001110000001000100000001011100010111010010011110,
+	    0b0111101110110101110111110000011000000100011100011000101101101110,
+	    0b1001100101111011011100011110101011001111100111101010101010110111,
+	    0b1100110010010001100011001111010000000100011101001111011101001111,
+	    0b1000111001111010100101000010000100000001001100101010001011001101,
+	    0b0011101011110000110010100101010100110010100001000010101011111101,
+	    0b1100000000000110000010101011000000011101000110011111100010111111,
+	    0b0010100110000011011100010110111100010110101100110011101110001101,
+	    0b0010111101010011111000111001111100110111111100100011110001101110,
+	    0b1001110111001001101001001001011000010100110001000000100011010110,
+	    0b0011110101100111011011111100001000011001010100111100100101111010,
+	    0b0010001101000011000010100101110000010101101000100110000100001010,
+	    0b0010000010100110010101100101110011101111000111111111001001100001,
+	    0b0100111111011011011011100111111011000010011101101111011111110110,
+	    0b1111111111010110101011101000100101110100001110001001101011100111,
+	    0b1011111101000101110000111100100010111010100001010000010010110010,
+	    0b1111010101001011101011101010000100110110001110111100100110111111,
+	    0b1011001101000001001101000010101010010110010001100001011100011010,
+	    0b0101001011011101010001110100010000010001111100100100100001001101,
+	    0b0010100000111001100011000101100101000001111100111001101000000010,
+	    0b1011001111010101011001000100100110100100110111110100000110111000,
+	    0b0101011111010011100011010010111101110010100001111111100010001001,
+	    0b0010111011101100100000000000001111111010011101100111100001001101,
+	    0b1101000000000000000000000000000000000000000000000000000000000000 };
+    };
+#elif LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    struct floating_type_traits<long double>
+    {
+      static constexpr int mantissa_bits = 105;
+      static constexpr int exponent_bits = 11;
+      static constexpr bool has_implicit_leading_bit = true;
+      using mantissa_t = unsigned __int128;
+      using shortest_scientific_t = ryu::floating_decimal_128;
+
+      static constexpr uint64_t pow10_adjustment_tab[]
+	= { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	    0b0000000000000000000100000000000000000000001000000000000000000010,
+	    0b0000100000000000000000001001000000000000000001100100000000000000,
+	    0b0011000000000000000000000000000001110000010000000000000000000000,
+	    0b0000100000000000001000000000000000000000000000100000000000000000 };
+    };
+#endif
+
+  // An IEEE-style decomposition of a floating-point value of type T.
+  template<typename T>
+    struct ieee_t
+    {
+      typename floating_type_traits<T>::mantissa_t mantissa;
+      uint32_t biased_exponent;
+      bool sign;
+    };
+
+  // Decompose the floating-point value into its IEEE components.
+  template<typename T>
+    ieee_t<T>
+    get_ieee_repr(const T value)
+    {
+      constexpr int mantissa_bits = floating_type_traits<T>::mantissa_bits;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int total_bits = mantissa_bits + exponent_bits + 1;
+      constexpr auto get_uint_t = [] {
+	if constexpr (total_bits <= 32)
+	  return uint32_t{};
+	else if constexpr (total_bits <= 64)
+	  return uint64_t{};
+#ifdef __SIZEOF_INT128__
+	else if constexpr (total_bits <= 128)
+	  return (unsigned __int128){};
+#endif
+      };
+      using uint_t = decltype(get_uint_t());
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+
+      ieee_t<T> ieee_rep;
+      ieee_rep.mantissa = value_bits & ((uint_t{1} << mantissa_bits) - 1u);
+      ieee_rep.biased_exponent
+	= (value_bits >> mantissa_bits) & ((uint_t{1} << exponent_bits) - 1u);
+      ieee_rep.sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      return ieee_rep;
+    }
+
+#if LONG_DOUBLE_KIND == LDK_IBM128
+  template<>
+    ieee_t<long double>
+    get_ieee_repr(const long double value)
+    {
+      // The layout of __ibm128 isn't compatible with the standard IEEE format.
+      // So we transform it into an IEEE-compatible format, suitable for
+      // consumption by the generic Ryu API, with an 11-bit exponent and 105-bit
+      // mantissa (with an implicit leading bit).  We keep the exponent and sign
+      // of the high part, and merge the mantissa of the high part with the
+      // mantissa (and the implicit leading bit) of the low part.
+      using uint_t = unsigned __int128;
+      uint_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value_bits));
+
+      const uint64_t value_hi = value_bits;
+      const uint64_t value_lo = value_bits >> 64;
+
+      uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+      unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+      const int sign_hi = (value_hi >> 63) & 1;
+
+      uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+      const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+      const int sign_lo = (value_lo >> 63) & 1;
+
+	{
+	  // The following code for adjusting the low-part mantissa to combine
+	  // it with the high-part mantissa is taken from the glibc source file
+	  // sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	  mantissa_lo <<= 7;
+	  if (exponent_lo != 0)
+	    mantissa_lo |= (1ull << (52 + 7));
+	  else
+	    mantissa_lo <<= 1;
+
+	  const int ediff = exponent_hi - exponent_lo - 53;
+	  if (ediff > 63)
+	    mantissa_lo = 0;
+	  else if (ediff > 0)
+	    mantissa_lo >>= ediff;
+	  else if (ediff < 0)
+	    mantissa_lo <<= -ediff;
+
+	  if (sign_lo != sign_hi && mantissa_lo != 0)
+	    {
+	      mantissa_lo = (1ull << 60) - mantissa_lo;
+	      if (mantissa_hi == 0)
+		{
+		  mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		  mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		  exponent_hi--;
+		}
+	      else
+		mantissa_hi--;
+	    }
+	}
+
+      ieee_t<long double> ieee_rep;
+      ieee_rep.mantissa = ((uint_t{mantissa_hi} << 64)
+			   | (uint_t{mantissa_lo} << 4)) >> 11;
+      ieee_rep.biased_exponent = exponent_hi;
+      ieee_rep.sign = sign_hi;
+      return ieee_rep;
+    }
+#endif
+
+  // 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.0 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
+  // "rounded up" power of 10.
+  template<typename T>
+    bool
+    is_rounded_up_pow10_p(const typename
+			  floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)std::size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64]
+	      & (1ull << (63 - fd.exponent%64)));
+    }
+
+  int
+  get_mantissa_length(ryu::floating_decimal_32 fd)
+  { return ryu::decimalLength9(fd.mantissa); }
+
+  int
+  get_mantissa_length(ryu::floating_decimal_64 fd)
+  { return ryu::decimalLength17(fd.mantissa); }
+
+#ifdef __SIZEOF_INT128__
+  int
+  get_mantissa_length(ryu::floating_decimal_128 fd)
+  { return ryu::generic128::decimalLength(fd.mantissa); }
+#endif
+} // 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())
+	{
+	  if (!signbit(value))
+	    str.remove_prefix(strlen("-"));
+
+	  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 of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  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 bool has_implicit_leading_bit
+	= floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using mantissa_t = typename floating_type_traits<T>::mantissa_t;
+      constexpr int mantissa_t_width = sizeof(mantissa_t)*__CHAR_BIT__;
+
+      if (precision && *precision < 0)
+	precision = nullopt;
+
+      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.
+      const auto [ieee_mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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;
+      static_assert(mantissa_t_width >= rounded_mantissa_bits);
+      mantissa_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= mantissa_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (mantissa_t{1} << (mantissa_bits
+								     - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = mantissa_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_mantissa;
+	  const bitvec should_round = round_bit & (has_tail_bits | lsb_bit);
+
+	  const int dropped_bits = 4*(full_hex_precision - effective_precision);
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits;
+	  effective_mantissa <<= dropped_bits;
+	  if (should_round & (mantissa_t{1} << dropped_bits))
+	    {
+	      // Round up the least significant nibble.
+	      effective_mantissa += mantissa_t{1} << dropped_bits;
+	      // Check and adjust for overflow of the leading nibble.  When the
+	      // type has an implicit leading bit, then the leading nibble
+	      // before rounding is either 0 or 1, so it can't overflow.
+	      if constexpr (!has_implicit_leading_bit)
+		{
+		  // The only floating type with explicit leading mantissa bit
+		  // that we currently support is x86 80-bit extended precision.
+		  // So we hardcode the below overflow check for this type.
+		  static_assert(mantissa_t_width == 64
+				&& rounded_mantissa_bits == 64);
+		  if (effective_mantissa == 0)
+		    {
+		      // We rounded up the least significant nibble and the
+		      // mantissa overflowed, e.g f.fcp+10 with precision=1
+		      // became 10.0p+10.  Absorb the extra hexit into the
+		      // exponent to obtain 1.0p+14.
+		      effective_mantissa
+			= mantissa_t{1} << (rounded_mantissa_bits - 4);
+		      written_exponent += 4;
+		    }
+		}
+	    }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(mantissa_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(mantissa_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_printed_exponent = abs(written_exponent);
+      expected_output_length += (abs_printed_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_printed_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_printed_exponent >= 100 ? strlen("p+ddd")
+				 : abs_printed_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      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.
+	  int nibble_offset = rounded_mantissa_bits - 4;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+
+	       effective_mantissa &= ~(mantissa_t{0b1111} << nibble_offset);
+	       nibble_offset -= 4;
+	    }
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      auto result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point value.
+  template<typename T>
+    static typename floating_type_traits<T>::shortest_scientific_t
+    __floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (is_same_v<T, float>)
+	return ryu::floating_to_fd32(value);
+      else if constexpr (is_same_v<T, double>)
+	return ryu::floating_to_fd64(value);
+#ifdef __SIZEOF_INT128__
+      else if constexpr (is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = floating_type_traits<T>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = floating_type_traits<T>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = floating_type_traits<T>::has_implicit_leading_bit;
+
+	  const auto [mantissa, exponent, sign] = get_ieee_repr(value);
+	  return ryu::generic_binary_to_decimal(mantissa, exponent, sign,
+						  mantissa_bits, exponent_bits,
+						  !has_implicit_leading_bit);
+	}
+#else
+      static_assert(!is_same_v<T, long double>);
+#endif
+    }
+
+  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 = get_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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+NNNN")
+				     : abs_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, and so 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_rounded_up_pow10_p<T>(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
+	  // value_fits_inside_mantissa below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_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 if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __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;
+
+      // Extract the sign and exponent from the value.
+      const auto [mantissa, biased_exponent, sign] = get_ieee_repr(value);
+      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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+    // Ryu doesn't support formatting floating-point types larger than double
+    // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward obvious upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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.  Otherwise, our
+	  // floor(log2) is too coarse-grained and doesn't consider
+	  // precision-dependent rounding in order to distinguish between a
+	  // scientific exponent of +-100 or +-99.
+	  // We handle this edge case by first writing the result to a
+	  // sufficiently sized output buffer.
+	  const bool scientific_exponent_near_100
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+	  int output_length;
+	  if (scientific_exponent_near_100) [[unlikely]]
+	    {
+	      const int output_length_upper_bound
+		= strlen("-d.") + effective_precision + strlen("e+ddd");
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2exp_buffered_n(value, effective_precision,
+						    buffer, nullptr);
+	      __glibcxx_assert(output_length <= output_length_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.
+	      int expected_output_length = sign + strlen("d");
+	      if (effective_precision > 0)
+		expected_output_length += strlen(".") + effective_precision;
+	      expected_output_length
+		+= ((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_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      char buffer[output_length_upper_bound];
+	      output_length = ryu::d2fixed_buffered_n(value, effective_precision,
+						      buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  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) noexcept
+  { return __floating_to_chars_shortest(first, last, value, chars_format{}); }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+#if LONG_DOUBLE_KIND == LDK_BINARY64 || LONG_DOUBLE_KIND == LDK_UNSUPPORTED
+    return __floating_to_chars_shortest(first, last,
+					static_cast<double>(value),
+					chars_format{});
+#else
+    return __floating_to_chars_shortest(first, last, value, chars_format{});
+#endif
+  }
+
+  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) noexcept
+  { return __floating_to_chars_shortest(first, last, value, fmt); }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+#if LONG_DOUBLE_KIND == LDK_BINARY64 || LONG_DOUBLE_KIND == LDK_UNSUPPORTED
+    return __floating_to_chars_shortest(first, last,
+					static_cast<double>(value),
+					fmt);
+#else
+    return __floating_to_chars_shortest(first, last, value, fmt);
+#endif
+  }
+
+  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); }
+
+  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, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+#if LONG_DOUBLE_KIND == LDK_BINARY64 || LONG_DOUBLE_KIND == LDK_UNSUPPORTED
+    return __floating_to_chars_precision(first, last,
+					 static_cast<double>(value),
+					 fmt, precision);
+#else
+    return __floating_to_chars_precision(first, last, value, fmt, precision);
+#endif
+  }
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..d995aa02d09
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the rest of the formatting modes, which go through printf.
+void
+test02()
+{
+  const long double growth_factor = 1.442695040888963407359924681001892137L;
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (long double __value = 1.0L, count = 0; !isinf(__value);
+	 ++count <= 100.0L ? __value *= growth_factor : __value *= __value)
+      for (const long double value : {__value, 1.0L/__value})
+	{
+	  for (const int precision : {-1, 0, 10, 100, 10000})
+	    {
+	      const char* const printf_specifier
+		= (fmt == chars_format::fixed ? "%.*Lf"
+		   : fmt == chars_format::scientific ? "%.*Le"
+		   : fmt == chars_format::general ? "%.*Lg"
+		   : nullptr);
+	      unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						precision, value);
+
+	      char printf_buffer[output_length+1];
+	      snprintf(printf_buffer, output_length+1, printf_specifier,
+		       precision, value);
+
+	      char to_chars_buffer[output_length];
+	      auto result = to_chars(to_chars_buffer,
+				     to_chars_buffer+output_length,
+				     value, fmt, precision);
+	      VERIFY( result.ec == errc{} );
+	      VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+	      result = to_chars(to_chars_buffer,
+				to_chars_buffer+output_length-1,
+				value, fmt, precision);
+	      VERIFY( result.ec == errc::value_too_large );
+	    }
+
+	  // Verify that the nearby values have a different shortest form.
+	  char to_chars_buffer[50000];
+	  auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				 value, fmt);
+	  VERIFY( result.ec == errc{} );
+	  *result.ptr = '\0';
+	  char nearby_buffer[50000];
+	    {
+	      const long double smaller = nextdownl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				smaller, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+
+	    {
+	      long double larger = nextupl(value);
+	      result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				larger, fmt);
+	      VERIFY( result.ec == errc{} );
+	      *result.ptr = '\0';
+	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	    }
+	}
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
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");


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation
@ 2020-07-14 19:45 Patrick Palka
  0 siblings, 0 replies; 12+ messages in thread
From: Patrick Palka @ 2020-07-14 19:45 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:9f1aca2e63423844548fdd0034975983210b2bba

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

    libstdc++: Add floating-point std::to_chars implementation
    
    This implements the floating-point std::to_chars overloads for float,
    double and long double.  We use the Ryu library to compute the shortest
    round-trippable fixed and scientific forms of a number for float, double
    and long double.  We also use Ryu for performing fixed and scientific
    formatting of float and double. For formatting long double with an
    explicit precision argument we use a printf fallback.  Hexadecimal
    formatting for float, double and long double is implemented from
    scratch.
    
    The supported long double binary formats are float64 (same as double),
    float80 (x86 extended precision), float128 and ibm128.
    
    Much of the complexity of the implementation is in computing the exact
    output length before handing it off to Ryu (which doesn't do bounds
    checking).  In some cases it's hard to compute the output length before
    the fact, so in these cases we instead compute an upper bound on the
    output length and use a sufficiently-sized intermediate buffer (if the
    output range is smaller than the upper bound).
    
    Another source of complexity is in the general-with-precision formatting
    mode, where we need to do zero-trimming of the string returned by Ryu, and
    where we also take care to avoid having to format the string a second
    time when the general formatting mode resolves to fixed.
    
    Tested on x86_64-pc-linux-gnu, aarch64-unknown-linux-gnu,
    s390x-ibm-linux-gnu, and powerpc64-unknown-linux-gnu.
    
    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 floating-point
            overloads for float, double and long 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/20_util/to_chars/long_double.cc: New test.
            * testsuite/util/testsuite_abi.cc: Add new symbol version.

Diff:
---
 libstdc++-v3/acinclude.m4                          |    2 +-
 libstdc++-v3/config/abi/pre/gnu.ver                |   12 +
 libstdc++-v3/configure                             |    2 +-
 libstdc++-v3/include/std/charconv                  |   21 +
 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        | 1488 ++++++++++++++++++++
 .../testsuite/20_util/to_chars/long_double.cc      |  197 +++
 libstdc++-v3/testsuite/util/testsuite_abi.cc       |    3 +-
 9 files changed, 1726 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..9a1bcfd25d1 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2299,6 +2299,18 @@ GLIBCXX_3.4.28 {
 
 } GLIBCXX_3.4.27;
 
+GLIBCXX_3.4.29 {
+    # to_chars(char*, char*, [float|double|long double])
+    _ZSt8to_charsPcS_[fdeg];
+
+    # to_chars(char*, char*, [float|double|long double], chars_format)
+    _ZSt8to_charsPcS_[fdeg]St12chars_format;
+
+    # to_chars(char*, char*, [float|double|long double], chars_format, int)
+    _ZSt8to_charsPcS_[fdeg]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..042cc4fbcca 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -688,6 +688,27 @@ namespace __detail
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
+  // Floating-point std::to_chars
+
+  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, long 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, long 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;
+  to_chars_result to_chars(char* __first, char* __last, long 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..1bafdd8bf29
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -0,0 +1,1488 @@
+// 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 <bit>
+#include <cfenv>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <langinfo.h>
+#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 __generic128
+    {
+      // Put the generic Ryu definitions in its own namespace to avoid name
+      // conflicts.
+#include "ryu/ryu_generic_128.h"
+#include "ryu/generic_128.h"
+#include "ryu/generic_128.c"
+    } // namespace __generic128
+
+    using __generic128::floating_decimal_128;
+    using __generic128::generic_binary_to_decimal;
+
+    int
+    to_chars(const floating_decimal_128 v, char* const result)
+    { return __generic128::generic_to_chars(v, result); }
+  } // 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(strlen("-"));
+
+	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{}}};
+      }
+  }
+
+  namespace
+  {
+    enum class __long_double_kind
+    {
+      float80,     // x86 extended precision
+      ibm128,      // ibm 128bit (encoded as a pair of doubles)
+      float128,    // ieee binary128
+      float64,     // ieee binary64, same as double
+      unsupported, // unsupported/unknown
+    };
+
+    // Determine the binary format of our 'long double' type, if we support it.
+    // If the binary format is 'unsupported' or 'float64', then we make the long
+    // double to_chars overloads just go through double.
+    constexpr __long_double_kind __ldk
+      = (__LDBL_MANT_DIG__ == 64 ? __long_double_kind::float80
+	 : __LDBL_MANT_DIG__ == 106 ? __long_double_kind::ibm128
+	 : __LDBL_MANT_DIG__ == 113 ? __long_double_kind::float128
+	 : __LDBL_MANT_DIG__ == __DBL_MANT_DIG__ ? __long_double_kind::float64
+	 : __long_double_kind::unsupported);
+
+    // A traits class that contains pertinent information about the binary
+    // format of each of the floating-point types we support.
+    template<typename T, __long_double_kind ldk = __ldk>
+      struct __floating_type_traits
+      { };
+
+    template<>
+      struct __floating_type_traits<float>
+      {
+	// We (and Ryu) assume float has the IEEE binary32 format.
+	static_assert(__FLT_MANT_DIG__ == 24);
+
+	using bitrep_t = uint32_t;
+	static constexpr int mantissa_bits = 23;
+	static constexpr int exponent_bits = 8;
+	static constexpr bool has_implicit_leading_bit = true;
+
+	using shortest_scientific_t = __ryu::floating_decimal_32;
+	static constexpr uint64_t pow10_adjustment_tab[]
+	  = { 0b0000000000011101011100110101100101101110000000000000000000000000,
+	    };
+      };
+
+    template<>
+      struct __floating_type_traits<double>
+      {
+	// We (and Ryu) assume double has the IEEE binary64 format.
+	static_assert(__DBL_MANT_DIG__ == 53);
+
+	using bitrep_t = uint64_t;
+	static constexpr int mantissa_bits = 52;
+	static constexpr int exponent_bits = 11;
+	static constexpr bool has_implicit_leading_bit = true;
+
+	using shortest_scientific_t = __ryu::floating_decimal_64;
+	static constexpr uint64_t pow10_adjustment_tab[]
+	  = { 0b0000000000000000000000011000110101110111000001100101110000111100,
+	      0b0111100011110101011000011110000000110110010101011000001110011111,
+	      0b0101101100000000011100100100111100110110110100010001010101110000,
+	      0b0011110010111000101111110101100011101100010001010000000101100111,
+	      0b0001010000011001011100100001010000010101101000001101000000000000,
+	    };
+      };
+
+    template<>
+      struct __floating_type_traits<long double, __long_double_kind::float64>
+      {
+	// These traits are deliberately empty since we'll make the exported
+	// long double overloads just call the double overloads.
+      };
+
+    template<>
+      struct __floating_type_traits<long double, __long_double_kind::float80>
+      {
+	using bitrep_t = unsigned __int128;
+	static constexpr int mantissa_bits = 64;
+	static constexpr int exponent_bits = 15;
+	static constexpr bool has_implicit_leading_bit = false;
+
+	using shortest_scientific_t = __ryu::floating_decimal_128;
+	static constexpr uint64_t pow10_adjustment_tab[]
+	  = { 0b0000000000000000000000000000110101011111110100010100110000011101,
+	      0b1001100101001111010011011111101000101111110001011001011101110000,
+	      0b0000101111111011110010001000001010111101011110111111010100011001,
+	      0b0011100000011111001101101011111001111100100010000101001111101001,
+	      0b0100100100000000100111010010101110011000110001101101110011001010,
+	      0b0111100111100010100000010011000010010110101111110101000011110100,
+	      0b1010100111100010011110000011011101101100010110000110101010101010,
+	      0b0000001111001111000000101100111011011000101000110011101100110010,
+	      0b0111000011100100101101010100001101111110101111001000010011111111,
+	      0b0010111000100110100100100010101100111010110001101010010111001000,
+	      0b0000100000010110000011001001000111000001111010100101101000001111,
+	      0b0010101011101000111100001011000010011101000101010010010000101111,
+	      0b1011111011101101110010101011010001111000101000101101011001100011,
+	      0b1010111011011011110111110011001010000010011001110100101101000101,
+	      0b0011000001110110011010010000011100100011001011001100001101010110,
+	      0b0100011111011000111111101000011110000010111110101001000000001001,
+	      0b1110000001110001001101101110011000100000001010000111100010111010,
+	      0b1110001001010011101000111000001000010100110000010110100011110000,
+	      0b0000011010110000110001111000011111000011001101001101001001000110,
+	      0b1010010111001000101001100101010110100100100010010010000101000010,
+	      0b1011001110000111100010100110000011100011111001110111001100000101,
+	      0b0110101001001000010110001000010001010101110101100001111100011001,
+	      0b1111100011110101011110011010101001010010100011000010110001101001,
+	      0b0100000100001000111101011100010011011111011001000000001100011000,
+	      0b1110111111000111100101110111110000000011001110011100011011011001,
+	      0b1100001100100000010001100011011000111011110000110011010101000011,
+	      0b1111111011100111011101001111111000010000001111010111110010000100,
+	      0b1110111001111110101111000101000000001010001110011010001000111010,
+	      0b1000010001011000101111111010110011111101110101101001111000111010,
+	      0b0100000111101001000111011001101000001010111011101001101111000100,
+	      0b0000011100110001000111011100111100110001101111111010110111100000,
+	      0b0000011101011100100110010011110101010100010011110010010111010000,
+	      0b0011011001100111110101111100001001101110101101001110110011110110,
+	      0b1011000101000001110100111001100100111100110011110000000001101000,
+	      0b1011100011110100001001110101010110111001000000001011101001011110,
+	      0b1111001010010010100000010110101010101011101000101000000000001100,
+	      0b1000001111100100111001110101100001010011111111000001000011110000,
+	      0b0001011101001000010000101101111000001110101100110011001100110111,
+	      0b1110011100000010101011011111001010111101111110100000011100000011,
+	      0b1001110110011100101010011110100010110001001110110000101011100110,
+	      0b1001101000100011100111010000011011100001000000110101100100001001,
+	      0b1010111000101000101101010111000010001100001010100011111100000100,
+	      0b0111101000100011000101101011111011100010001101110111001111001011,
+	      0b1110100111010110001110110110000000010110100011110000010001111100,
+	      0b1100010100011010001011001000111001010101011110100101011001000000,
+	      0b0000110001111001100110010110111010101101001101000000000010010101,
+	      0b0001110111101000001111101010110010010000111110111100000111110100,
+	      0b0111110111001001111000110001101101001010101110110101111110000100,
+	      0b0000111110111010101111100010111010011100010110011011011001000001,
+	      0b1010010100100100101110111111111000101100000010111111101101000110,
+	      0b1000100111111101100011001101000110001000000100010101010100001101,
+	      0b1100101010101000111100101100001000110001110010100000000010110101,
+	      0b1010000100111101100100101010010110100010000000110101101110000100,
+	      0b1011111011110001110000100100000000001010111010001101100000100100,
+	      0b0111101101100011001110011100000001000101101101111000100111011111,
+	      0b0100111010010011011001010011110100001100111010010101111111100011,
+	      0b0010001001011000111000001100110111110111110010100011000110110110,
+	      0b0101010110000000010000100000110100111011111101000100000111010010,
+	      0b0110000011011101000001010100110101101110011100110101000000001001,
+	      0b1101100110100000011000001111000100100100110001100110101010101100,
+	      0b0010100101010110010010001010101000011111111111001011001010001111,
+	      0b0111001010001111001100111001010101001000110101000011110000001000,
+	      0b0110010011001001001111110001010010001011010010001101110110110011,
+	      0b0110010100111011000100111000001001101011111001110010111110111111,
+	      0b0101110111001001101100110100101001110010101110011001101110001000,
+	      0b0100110101010111011010001100010111100011010011111001010100111000,
+	      0b0111000110110111011110100100010111000110000110110110110001111110,
+	      0b1000101101010100100100111110100011110110110010011001110011110101,
+	      0b1001101110101001010100111101101011000101000010110101101111110000,
+	      0b0100100101001011011001001011000010001101001010010001010110101000,
+	      0b0010100001001011100110101000010110000111000111000011100101011011,
+	      0b0110111000011001111101101011111010001000000010101000101010011110,
+	      0b1000110110100001111011000001111100001001000000010110010100100100,
+	      0b1001110100011111100111101011010000010101011100101000010010100110,
+	      0b0001010110101110100010101010001110110110100011101010001001111100,
+	      0b1010100101101100000010110011100110100010010000100100001110000100,
+	      0b0001000000010000001010000010100110000001110100111001110111101101,
+	      0b1100000000000000000000000000000000000000000000000000000000000000,
+	    };
+      };
+
+    template<>
+      struct __floating_type_traits<long double, __long_double_kind::ibm128>
+      {
+	using bitrep_t = unsigned __int128;
+	static constexpr int mantissa_bits = 105;
+	static constexpr int exponent_bits = 11;
+	static constexpr bool has_implicit_leading_bit = true;
+
+	using shortest_scientific_t = __ryu::floating_decimal_128;
+	static constexpr uint64_t pow10_adjustment_tab[]
+	  = { 0b0000000000000000000000000000000000000000000000001000000100000000,
+	      0b0000000000000000000100000000000000000000001000000000000000000010,
+	      0b0000100000000000000000001001000000000000000001100100000000000000,
+	      0b0011000000000000000000000000000001110000010000000000000000000000,
+	      0b0000100000000000001000000000000000000000000000100000000000000000,
+	    };
+      };
+
+    template<>
+      struct __floating_type_traits<long double, __long_double_kind::float128>
+      {
+	using bitrep_t = unsigned __int128;
+	static constexpr int mantissa_bits = 112;
+	static constexpr int exponent_bits = 15;
+	static constexpr bool has_implicit_leading_bit = true;
+
+	using shortest_scientific_t = __ryu::floating_decimal_128;
+	static constexpr uint64_t pow10_adjustment_tab[]
+	  = { 0b0000000000000000000000000000000000000000000000000100000010000000,
+	      0b1011001111110100000100010101101110011100100110000110010110011000,
+	      0b1010100010001101111111000000001101010010100010010000111011110111,
+	      0b1011111001110001111000011111000010110111000111110100101010100101,
+	      0b0110100110011110011011000011000010011001110001001001010011100011,
+	      0b0000011111110010101111101011101010000110011111100111001110100111,
+	      0b0100010101010110000010111011110100000010011001001010001110111101,
+	      0b1101110111000010001101100000110100000111001001101011000101011011,
+	      0b0100111011101101010000001101011000101100101110010010110000101011,
+	      0b0100000110111000000110101000010011101000110100010110000011101101,
+	      0b1011001101001000100001010001100100001111011101010101110001010110,
+	      0b1000000001000000101001110010110010001111101101010101001100000110,
+	      0b0101110110100110000110000001001010111110001110010000111111010011,
+	      0b1010001111100111000100011100100100111100100101000001011001000111,
+	      0b1010011000011100110101100111001011100101111111100001110100000100,
+	      0b1100011100100010100000110001001010000000100000001001010111011101,
+	      0b0101110000100011001111101101000000100110000010010111010001111010,
+	      0b0100111100011010110111101000100110000111001001101100000001111100,
+	      0b1100100100111110101011000100000101011010110111000111110100110101,
+	      0b0110010000010111010100110011000000111010000010111011010110000100,
+	      0b0101001001010010110111010111000101011100000111100111000001110010,
+	      0b1101111111001011101010110001000111011010111101001011010110100100,
+	      0b0001000100110000011111101011001101110010110110010000000011100100,
+	      0b0001000000000101001001001000000000011000100011001110101001001110,
+	      0b0010010010001000111010011011100001000110011011011110110100111000,
+	      0b0000100110101100000111100010100100011100110111011100001111001100,
+	      0b1011111010001110001100000011110111111111100000001011111111101100,
+	      0b0000011100001111010101110000100110111100101101110111101001000001,
+	      0b1100010001110110111100001001001101101000011100000010110101001011,
+	      0b0100101001101011111001011110101101100011011111011100101010101111,
+	      0b0001101001111001110000101101101100001011010001011110011101000010,
+	      0b1111000000101001101111011010110011101110100001011011001011100010,
+	      0b0101001010111101101100001111100010010110001101001000001101100100,
+	      0b0101100101011110001100101011111000111001111001001001101101100001,
+	      0b1111001101010010100100011011000110110010001111000111010001001101,
+	      0b0001110010011000000001000110110111011000011100001000011001110111,
+	      0b0100001011011011011011110011101100100101111111101100101000001110,
+	      0b0101011110111101010111100111101111000101111111111110100011011010,
+	      0b1110101010001001110100000010110111010111111010111110100110010110,
+	      0b1010001111100001001100101000110100001100011100110010000011010111,
+	      0b1111111101101111000100111100000101011000001110011011101010111001,
+	      0b1111101100001110100101111101011001000100000101110000110010100011,
+	      0b1001010110110101101101000101010001010000101011011111010011010000,
+	      0b0111001110110011101001100111000001000100001010110000010000001101,
+	      0b0101111100111110100111011001111001111011011110010111010011101010,
+	      0b1110111000000001100100111001100100110001011011001110101111110111,
+	      0b0001010001001101010111101010011111000011110001101101011001111111,
+	      0b0101000011100011010010001101100001011101011010100110101100100010,
+	      0b0001000101011000100101111100110110000101101101111000110001001011,
+	      0b0101100101001011011000010101000000010100011100101101000010011111,
+	      0b1000010010001011101001011010100010111011110100110011011000100111,
+	      0b1000011011100001010111010111010011101100100010010010100100101001,
+	      0b1001001001010111110101000010111010000000101111010100001010010010,
+	      0b0011011110110010010101111011000001000000000011011111000011111011,
+	      0b1011000110100011001110000001000100000001011100010111010010011110,
+	      0b0111101110110101110111110000011000000100011100011000101101101110,
+	      0b1001100101111011011100011110101011001111100111101010101010110111,
+	      0b1100110010010001100011001111010000000100011101001111011101001111,
+	      0b1000111001111010100101000010000100000001001100101010001011001101,
+	      0b0011101011110000110010100101010100110010100001000010101011111101,
+	      0b1100000000000110000010101011000000011101000110011111100010111111,
+	      0b0010100110000011011100010110111100010110101100110011101110001101,
+	      0b0010111101010011111000111001111100110111111100100011110001101110,
+	      0b1001110111001001101001001001011000010100110001000000100011010110,
+	      0b0011110101100111011011111100001000011001010100111100100101111010,
+	      0b0010001101000011000010100101110000010101101000100110000100001010,
+	      0b0010000010100110010101100101110011101111000111111111001001100001,
+	      0b0100111111011011011011100111111011000010011101101111011111110110,
+	      0b1111111111010110101011101000100101110100001110001001101011100111,
+	      0b1011111101000101110000111100100010111010100001010000010010110010,
+	      0b1111010101001011101011101010000100110110001110111100100110111111,
+	      0b1011001101000001001101000010101010010110010001100001011100011010,
+	      0b0101001011011101010001110100010000010001111100100100100001001101,
+	      0b0010100000111001100011000101100101000001111100111001101000000010,
+	      0b1011001111010101011001000100100110100100110111110100000110111000,
+	      0b0101011111010011100011010010111101110010100001111111100010001001,
+	      0b0010111011101100100000000000001111111010011101100111100001001101,
+	      0b1101000000000000000000000000000000000000000000000000000000000000,
+	    };
+      };
+  } // anon namespace
+
+  // Returns the bit representation of the floating-point value argument that
+  // is compatible with the canonical IEEE format.
+  template<typename T>
+    static typename __floating_type_traits<T>::bitrep_t
+    __get_canonical_bitrep(const T value)
+    {
+      typename __floating_type_traits<T>::bitrep_t value_bits = 0;
+      memcpy(&value_bits, &value, sizeof(value));
+      return value_bits;
+    }
+
+  template<>
+    typename __floating_type_traits<long double>::bitrep_t
+    __get_canonical_bitrep(const long double value)
+    {
+      if constexpr (__ldk != __long_double_kind::ibm128)
+	{
+	  typename __floating_type_traits<long double>::bitrep_t value_bits = 0;
+	  memcpy(&value_bits, &value, sizeof(value));
+	  return value_bits;
+	}
+      else
+	{
+	  // The layout of __ibm128 isn't compatible with the canonical IEEE
+	  // format.  So we transform it into an IEEE-compatible format,
+	  // suitable for consumption by the generic Ryu API, with an 11-bit
+	  // exponent and 105-bit mantissa (with an implicit leading bit).
+	  // We keep the exponent and sign of the high part, and merge the
+	  // mantissa of the high part with the mantissa (and the implicit
+	  // leading bit) of the low part.
+	  using bitrep_t = unsigned __int128;
+	  bitrep_t value_bits = 0;
+	  memcpy(&value_bits, &value, sizeof(value_bits));
+
+	  const uint64_t value_hi = value_bits;
+	  const uint64_t value_lo = value_bits >> 64;
+
+	  uint64_t mantissa_hi = value_hi & ((1ull << 52) - 1);
+	  unsigned exponent_hi = (value_hi >> 52) & ((1ull << 11) - 1);
+	  const int sign_hi = (value_hi >> 63) & 1;
+
+	  uint64_t mantissa_lo = value_lo & ((1ull << 52) - 1);
+	  const unsigned exponent_lo = (value_lo >> 52) & ((1ull << 11) - 1);
+	  const int sign_lo = (value_lo >> 63) & 1;
+
+	    {
+	      // The following code for adjusting the low-part mantissa to
+	      // combine it with the high-part mantissa is taken from the glibc
+	      // source file sysdeps/ieee754/ldbl-128ibm/printf_fphex.c.
+	      mantissa_lo <<= 7;
+	      if (exponent_lo != 0)
+		mantissa_lo |= (1ull << (52 + 7));
+	      else
+		mantissa_lo <<= 1;
+
+	      const int ediff = exponent_hi - exponent_lo - 53;
+	      if (ediff > 63)
+		mantissa_lo = 0;
+	      else if (ediff > 0)
+		mantissa_lo >>= ediff;
+	      else if (ediff < 0)
+		mantissa_lo <<= -ediff;
+
+	      if (sign_lo != sign_hi && mantissa_lo != 0)
+		{
+		  mantissa_lo = (1ull << 60) - mantissa_lo;
+		  if (mantissa_hi == 0)
+		    {
+		      mantissa_hi = 0xffffffffffffeLL | (mantissa_lo >> 59);
+		      mantissa_lo = 0xfffffffffffffffLL & (mantissa_lo << 1);
+		      exponent_hi--;
+		    }
+		  else
+		    mantissa_hi--;
+		}
+	    }
+
+	  const int ieee_sign = sign_hi;
+	  const unsigned ieee_exponent = exponent_hi;
+	  const bitrep_t ieee_mantissa = ((bitrep_t{mantissa_hi} << 64)
+					  | (bitrep_t{mantissa_lo} << 4)) >> 11;
+
+	  bitrep_t ieee_bits = 0;
+	  if (ieee_sign)
+	    ieee_bits |= bitrep_t{1} << (11+105);
+	  ieee_bits |= bitrep_t{ieee_exponent} << 105;
+	  ieee_bits |= ieee_mantissa;
+	  return ieee_bits;
+	}
+    }
+
+  // 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
+  // "rounded up" power of 10.
+  template<typename T>
+    static bool
+    __is_rounded_up_pow10_p(const typename
+			    __floating_type_traits<T>::shortest_scientific_t fd)
+    {
+      if (fd.exponent < 0 || fd.mantissa != 1) [[likely]]
+	return false;
+
+      constexpr auto& pow10_adjustment_tab
+	= __floating_type_traits<T>::pow10_adjustment_tab;
+      __glibcxx_assert(fd.exponent/64 < (int)size(pow10_adjustment_tab));
+      return (pow10_adjustment_tab[fd.exponent/64] & (1ull<<(63-fd.exponent%64)));
+    }
+
+  // This subroutine of the floating-point to_chars overloads performs
+  // hexadecimal formatting.
+  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 bool has_implicit_leading_bit
+	= __floating_type_traits<T>::has_implicit_leading_bit;
+      constexpr int exponent_bits = __floating_type_traits<T>::exponent_bits;
+      constexpr int exponent_bias = (1u << (exponent_bits - 1)) - 1;
+      using bitrep_t = typename __floating_type_traits<T>::bitrep_t;
+
+      if (precision && *precision < 0)
+	precision = nullopt;
+
+      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.
+      const auto value_bits = __get_canonical_bitrep(value);
+      const bool sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      const bitrep_t ieee_mantissa = value_bits & ((bitrep_t{1} << mantissa_bits)
+						   - 1u);
+      const uint32_t biased_exponent
+	= (value_bits >> mantissa_bits) & ((bitrep_t{1} << 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;
+      bitrep_t effective_mantissa
+	= ieee_mantissa << (rounded_mantissa_bits - mantissa_bits);
+      if (is_normal_number)
+	{
+	  if constexpr (has_implicit_leading_bit)
+	    // Restore the mantissa's implicit leading bit.
+	    effective_mantissa |= bitrep_t{1} << rounded_mantissa_bits;
+	  else
+	    // The explicit mantissa bit should already be set.
+	    __glibcxx_assert(effective_mantissa & (bitrep_t{1} << (mantissa_bits
+								   - 1u)));
+	}
+
+      // Compute the shortest precision needed to print this value exactly,
+      // disregarding trailing zeros.
+      constexpr int full_hex_precision = (has_implicit_leading_bit
+					  ? (mantissa_bits + 3) / 4
+					  // With an explicit leading bit, we
+					  // use the four leading nibbles as the
+					  // hexit before the decimal point.
+					  : (mantissa_bits - 4 + 3) / 4);
+      const int trailing_zeros = __countr_zero(effective_mantissa) / 4;
+      const int shortest_full_precision = full_hex_precision - trailing_zeros;
+      __glibcxx_assert(shortest_full_precision >= 0);
+
+      int written_exponent = unbiased_exponent;
+      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 = bitrep_t;
+	  const bitvec round_bit = effective_mantissa << 1;
+	  const bitvec has_tail_bits = round_bit - 1;
+	  const bitvec lsb_bit = effective_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 & (bitrep_t{1} << dropped_bits))
+	    // Round up the least significant printed hexit.
+	    effective_mantissa += bitrep_t{1} << dropped_bits;
+	  // Mask out the dropped nibbles.
+	  effective_mantissa >>= dropped_bits,
+	  effective_mantissa <<= dropped_bits;
+	  if constexpr (!has_implicit_leading_bit)
+	    if (effective_mantissa == bitrep_t{1} << rounded_mantissa_bits)
+	      {
+		// We rounded the leading nibble up and it overflowed,
+		// e.g f.ffp+10 with precision=1 became 10.0p+10.  Absorb the
+		// extra hexit into the exponent to obtain 1.0p+14.
+		effective_mantissa >>= 4;
+		written_exponent += 4;
+	      }
+	}
+
+      // Compute the leading hexit and mask it out from the mantissa.
+      char leading_hexit;
+      if constexpr (has_implicit_leading_bit)
+	{
+	  const unsigned nibble = effective_mantissa >> rounded_mantissa_bits;
+	  __glibcxx_assert(nibble <= 2);
+	  leading_hexit = '0' + nibble;
+	  effective_mantissa &= ~(bitrep_t{0b11} << rounded_mantissa_bits);
+	}
+      else
+	{
+	  const unsigned nibble = effective_mantissa >> (rounded_mantissa_bits-4);
+	  __glibcxx_assert(nibble < 16);
+	  leading_hexit = "0123456789abcdef"[nibble];
+	  effective_mantissa &= ~(bitrep_t{0b1111} << (rounded_mantissa_bits-4));
+	  written_exponent -= 3;
+	}
+
+      // 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_printed_exponent = abs(written_exponent);
+      expected_output_length += (abs_printed_exponent >= 10000 ? strlen("p+ddddd")
+				 : abs_printed_exponent >= 1000 ? strlen("p+dddd")
+				 : abs_printed_exponent >= 100 ? strlen("p+ddd")
+				 : abs_printed_exponent >= 10 ? strlen("p+dd")
+				 : strlen("p+d"));
+      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.
+	  int nibble_offset = rounded_mantissa_bits - 4;
+	  if constexpr (!has_implicit_leading_bit)
+	    // We already printed the entire leading hexit.
+	    nibble_offset -= 4;
+	  while (effective_mantissa != 0)
+	    {
+	      const unsigned nibble = effective_mantissa >> nibble_offset;
+	      __glibcxx_assert(nibble < 16);
+	      *first++ = "0123456789abcdef"[nibble];
+	      ++written_hexits;
+
+	       effective_mantissa &= ~(bitrep_t{0b1111} << nibble_offset);
+	       nibble_offset -= 4;
+	    }
+	  __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 (written_exponent >= 0)
+	*first++ = '+';
+      auto result = to_chars(first, last, written_exponent);
+      __glibcxx_assert(result.ec == errc{}
+		       && result.ptr == saved_first + expected_output_length);
+      return result;
+    }
+
+  // Invoke Ryu to obtain the shortest scientific form for the given
+  // floating-point value.
+  template<typename T>
+    static typename __floating_type_traits<T>::shortest_scientific_t
+    __floating_to_shortest_scientific(const T value)
+    {
+      if constexpr (is_same_v<T, float>)
+	return __ryu::floating_to_fd32(value);
+      else if constexpr (is_same_v<T, double>)
+	return __ryu::floating_to_fd64(value);
+      else if constexpr (is_same_v<T, long double>)
+	{
+	  constexpr int mantissa_bits
+	    = __floating_type_traits<long double>::mantissa_bits;
+	  constexpr int exponent_bits
+	    = __floating_type_traits<long double>::exponent_bits;
+	  constexpr bool has_implicit_leading_bit
+	    = __floating_type_traits<long double>::has_implicit_leading_bit;
+
+	  const auto value_bits = __get_canonical_bitrep(value);
+	  return __ryu::generic_binary_to_decimal(value_bits,
+						  mantissa_bits, exponent_bits,
+						  !has_implicit_leading_bit);
+	}
+    }
+
+  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); }
+
+  static int
+  __mantissa_length(__ryu::floating_decimal_128 fd)
+  { return __ryu::__generic128::decimalLength(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 will be 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(".");
+	  const int abs_exponent = abs(scientific_exponent);
+	  expected_output_length += (abs_exponent >= 1000 ? strlen("e+NNNN")
+				     : abs_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, and so 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_rounded_up_pow10_p<T>(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
+	  // value_fits_inside_mantissa below.
+	  const int log2_mantissa = __bit_width(fd.mantissa) - 1;
+	  const bool value_fits_inside_mantissa
+	    = (log2_mantissa + (fd.exponent*10 + 2) / 3
+	       < __floating_type_traits<T>::mantissa_bits - 2);
+	  if (value_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 if constexpr (is_same_v<T, long double>)
+	    {
+	      // We can't use d2fixed_buffered_n for types larger than double,
+	      // so we instead format larger types through sprintf.
+	      // TODO: We currently go through an intermediate buffer in order
+	      // to accomodate the mandatory null terminator of sprintf, but we
+	      // can avoid this if we use sprintf to write all but the last
+	      // digit, and carefully compute and write the last digit
+	      // ourselves.
+	      char buffer[expected_output_length+1];
+	      const int saved_rounding_mode = fegetround();
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	      const int output_length = sprintf(buffer, "%.0Lf", value);
+	      if (saved_rounding_mode != FE_TONEAREST)
+		fesetround(saved_rounding_mode);
+	      __glibcxx_assert(output_length == expected_output_length);
+	      memcpy(first, buffer, output_length);
+	      return {first + output_length, errc{}};
+	    }
+	  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;
+	    }
+	}
+
+      __glibcxx_assert(false);
+      __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 bitrep_t = typename __floating_type_traits<T>::bitrep_t;
+
+      // Extract the sign and exponent from the value.
+      const auto value_bits = __get_canonical_bitrep(value);
+      const bool sign = (value_bits >> (mantissa_bits + exponent_bits)) & 1;
+      const uint32_t biased_exponent
+	= (value_bits >> mantissa_bits) & ((bitrep_t{1} << 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.
+      // ???: Using "mantissa_bits + 1" instead of just "mantissa_bits" in the
+      // bounds below is necessary only for __ibm128, it seems.  Even though the
+      // type has 105 bits of precision, printf may output 106 fractional digits
+      // on some inputs, e.g. 0x1.bcd19f5d720d12a3513e3301028p+0.
+      const int max_eff_scientific_precision
+	= (floor_log2_value >= 0
+	   ? max(mantissa_bits + 1, approx_log10_value + 1)
+	   : -(7*floor_log2_value + 9)/10 + 2 + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_scientific_precision > 0);
+
+      const int max_eff_fixed_precision
+	= (floor_log2_value >= 0
+	   ? mantissa_bits + 1
+	   : -floor_log2_value + mantissa_bits + 1);
+      __glibcxx_assert(max_eff_fixed_precision > 0);
+
+    // Ryu doesn't support formatting floating-point types larger than double
+    // with an explicit precision, so instead we just go through printf.
+      if constexpr (is_same_v<T, long double>)
+	{
+	  int effective_precision;
+	  const char* output_specifier;
+	  if (fmt == chars_format::scientific)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Le";
+	    }
+	  else if (fmt == chars_format::fixed)
+	    {
+	      effective_precision = min(precision, max_eff_fixed_precision);
+	      output_specifier = "%.*Lf";
+	    }
+	  else if (fmt == chars_format::general)
+	    {
+	      effective_precision = min(precision, max_eff_scientific_precision);
+	      output_specifier = "%.*Lg";
+	    }
+	  const int excess_precision = (fmt != chars_format::general
+					? precision - effective_precision : 0);
+
+	  // Since the output of printf is locale-sensitive, we need to be able
+	  // to handle a radix point that's different from '.'.
+	  char radix[6] = {'.', '\0', '\0', '\0', '\0', '\0'};
+	  if (effective_precision > 0)
+	    // ???: Can nl_langinfo() ever return null?
+	    if (const char* const radix_ptr = nl_langinfo(RADIXCHAR))
+	      {
+		strncpy(radix, radix_ptr, sizeof(radix)-1);
+		// We accept only radix points which are at most 4 bytes (one
+		// UTF-8 character) wide.
+		__glibcxx_assert(radix[4] == '\0');
+	      }
+
+	  // Compute straightforward obvious upper bounds on the output length.
+	  int output_length_upper_bound;
+	  if (fmt == chars_format::scientific || fmt == chars_format::general)
+	    output_length_upper_bound = (strlen("-d") + sizeof(radix)
+					 + effective_precision
+					 + strlen("e+dddd"));
+	  else if (fmt == chars_format::fixed)
+	    {
+	      if (approx_log10_value >= 0)
+		output_length_upper_bound = sign + approx_log10_value + 1;
+	      else
+		output_length_upper_bound = sign + strlen("0");
+	      output_length_upper_bound += sizeof(radix) + effective_precision;
+	    }
+
+	  // Do the sprintf into the local buffer.
+	  char buffer[output_length_upper_bound+1];
+	  const int saved_rounding_mode = fegetround();
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
+	  int output_length
+	    = sprintf(buffer, output_specifier, effective_precision, value);
+	  if (saved_rounding_mode != FE_TONEAREST)
+	    fesetround(saved_rounding_mode);
+	  __glibcxx_assert(output_length <= output_length_upper_bound);
+
+	  if (effective_precision > 0)
+	    // We need to replace a radix that is different from '.' with '.'.
+	    if (const string_view radix_sv = {radix}; radix_sv != ".")
+	      {
+		const string_view buffer_sv = {buffer, (size_t)output_length};
+		const size_t radix_index = buffer_sv.find(radix_sv);
+		if (radix_index != string_view::npos)
+		  {
+		    buffer[radix_index] = '.';
+		    if (radix_sv.length() > 1)
+		      {
+			memmove(&buffer[radix_index + 1],
+				&buffer[radix_index + radix_sv.length()],
+				output_length - radix_index - radix_sv.length());
+			output_length -= radix_sv.length() - 1;
+		      }
+		  }
+	      }
+
+	  // Copy the string from the buffer over to the output range.
+	  if (last - first < output_length + excess_precision)
+	    return {last, errc::value_too_large};
+	  memcpy(first, buffer, output_length);
+	  first += output_length;
+
+	  // Add the excess 0s to the result.
+	  if (excess_precision > 0)
+	    {
+	      if (fmt == chars_format::scientific)
+		{
+		  char* const significand_end
+		    = (output_length >= 6 && first[-6] == 'e' ? &first[-6]
+		       : 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;
+		}
+	      else if (fmt == chars_format::fixed)
+		{
+		  memset(first, '0', excess_precision);
+		  first += excess_precision;
+		}
+	    }
+	  return {first, errc{}};
+	}
+      else 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.  Otherwise, our
+	  // floor(log2) is too coarse-grained and doesn't consider
+	  // precision-dependent rounding in order to distinguish between a
+	  // scientific exponent of +-100 or +-99.
+	  // We handle this edge case by first writing the result to a
+	  // sufficiently sized output buffer.
+	  const bool scientific_exponent_near_100
+	    = abs(abs(floor_log2_value) - 332) <= 4;
+	  int output_length;
+	  if (scientific_exponent_near_100) [[unlikely]]
+	    {
+	      const int output_length_upper_bound
+		= strlen("-d.") + effective_precision + strlen("e+ddd");
+	      char buffer[output_length_upper_bound];
+	      output_length = __ryu::d2exp_buffered_n(value, effective_precision,
+						      buffer, nullptr);
+	      __glibcxx_assert(output_length <= output_length_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.
+	      int expected_output_length = sign + strlen("d");
+	      if (effective_precision > 0)
+		expected_output_length += strlen(".") + effective_precision;
+	      expected_output_length
+		+= ((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_length_upper_bound;
+	  if (approx_log10_value >= 0)
+	    output_length_upper_bound = sign + approx_log10_value + 1;
+	  else
+	    output_length_upper_bound = sign + strlen("0");
+	  if (effective_precision > 0)
+	    output_length_upper_bound += strlen(".") + effective_precision;
+	  int output_length;
+	  if (last - first >= output_length_upper_bound + excess_precision)
+	    {
+	      output_length
+		= __ryu::d2fixed_buffered_n(value, effective_precision, first);
+	      __glibcxx_assert(output_length <= output_length_upper_bound);
+	    }
+	  else
+	    {
+	      char buffer[output_length_upper_bound];
+	      output_length
+		= __ryu::d2fixed_buffered_n(value, effective_precision, buffer);
+	      __glibcxx_assert(output_length <= output_length_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_length_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_length_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_length_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_length_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{}};
+	}
+
+      __glibcxx_assert(false);
+      __builtin_unreachable();
+    }
+
+  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) noexcept
+  { return __floating_to_chars_shortest(first, last, value, chars_format{}); }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value) noexcept
+  {
+    if constexpr (__ldk == __long_double_kind::float64
+		  || __ldk == __long_double_kind::unsupported)
+      return __floating_to_chars_shortest(first, last, (double)value,
+					  chars_format{});
+    else
+      return __floating_to_chars_shortest(first, last, value, chars_format{});
+  }
+
+
+  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) noexcept
+  { return __floating_to_chars_shortest(first, last, value, fmt); }
+
+  to_chars_result
+  to_chars(char* first, char* last, long double value, chars_format fmt) noexcept
+  {
+    if constexpr (__ldk == __long_double_kind::float64
+		  || __ldk == __long_double_kind::unsupported)
+      return __floating_to_chars_shortest(first, last, (double)value, fmt);
+    else
+      return __floating_to_chars_shortest(first, last, value, fmt);
+  }
+
+
+  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); }
+
+  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, long double value, chars_format fmt,
+	   int precision) noexcept
+  {
+    if constexpr (__ldk == __long_double_kind::float64
+		  || __ldk == __long_double_kind::unsupported)
+      return __floating_to_chars_precision(first, last, (double)value,
+					   fmt, precision);
+    else
+      return __floating_to_chars_precision(first, last, value, fmt, precision);
+  }
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
new file mode 100644
index 00000000000..f6a73cf4b88
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -0,0 +1,197 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++17 } }
+
+#include <charconv>
+
+#include <cfenv>
+#include <cmath>
+#include <cstring>
+#include <iterator>
+#include <limits>
+
+#include <testsuite_hooks.h>
+
+using namespace std;
+
+// The long double overloads of std::to_chars currently just go through printf
+// (except for the hexadecimal formatting).
+
+// Test our hand-written hexadecimal formatting implementation.
+void
+test01()
+{
+  const long double hex_testcases[]
+    = { nextdownl(numeric_limits<long double>::max()),
+	nextupl(numeric_limits<long double>::min()),
+	42.0L,
+	0x1.2p+0L,
+	0x1.23p+0L,
+	0x1.234p+0L,
+	0x1.2345p+0L,
+	0x1.23456p+0L,
+	0x1.234567p+0L,
+	0x1.2345678p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789p+0L,
+	0x1.23456789ap+0L,
+	0x1.23456789abp+0L,
+	0x1.23456789abcp+0L,
+	0x1.23456789abcdp+0L,
+	0x1.23456789abcdep+0L,
+	0x1.23456789abcdefp+0L,
+	0x1.23456789abcdef0p+0L,
+	0x1.23456789abcdef01p+0L,
+	0x1.23456789abcdef012p+0L,
+	0x1.23456789abcdef0123p+0L,
+	0x1.23456789abcdef01234p+0L,
+	0x1.23456789abcdef012345p+0L,
+	0x1.23456789abcdef0123456p+0L,
+	0x1.23456789abcdef01234567p+0L,
+	0x1.23456789abcdef012345678p+0L,
+	0x1.23456789abcdef0123456789p+0L,
+	0x1.23456789abcdef0123456789ap+0L,
+	0x1.23456789abcdef0123456789abp+0L,
+	0x1.23456789abcdef0123456789abcp+0L,
+	0x1.23456789abcdef0123456789abcdp+0L,
+    };
+
+  for (int exponent : {-11000, -3000, -300, -50, -7, 0, 7, 50, 300, 3000, 11000})
+    for (long double testcase : hex_testcases)
+      {
+	testcase = ldexpl(testcase, exponent);
+	if (testcase == 0.0L || isinf(testcase))
+	  continue;
+
+	char to_chars_buffer[1024], printf_buffer[1024];
+	memset(to_chars_buffer, '\0', sizeof(to_chars_buffer));
+	memset(printf_buffer, '\0', sizeof(printf_buffer));
+
+	auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			       testcase, chars_format::hex);
+	VERIFY( result.ec == errc{} );
+	*result.ptr = '\0';
+	sprintf(printf_buffer, "%La", testcase);
+	VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	  {
+	    // Test that the nearby values have a different shortest form.
+	    testcase = nextdownl(testcase);
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextupl(nextupl(testcase));
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0);
+	    sprintf(printf_buffer, "%La", testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+
+	    testcase = nextdownl(testcase);
+	  }
+
+	for (int precision = -1; precision < 50; precision++)
+	  {
+	    result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+			      testcase, chars_format::hex, precision);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    sprintf(printf_buffer, "%.*La", precision, testcase);
+	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
+	  }
+      }
+}
+
+// Test the other formatting modes, which go through printf.
+void
+test02()
+{
+  for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
+			   chars_format::general})
+    for (const long double start : {2.0L, 0x1.ffffffffffffffffffffffffffffp+0L})
+      for (long double __value = start; !isinf(__value); __value *= __value)
+	for (const long double value : {__value, 1.0L/__value})
+	  {
+	    for (const int precision : {-1, 0, 10, 100, 10000})
+	      {
+		const char* const printf_specifier
+		  = (fmt == chars_format::fixed ? "%.*Lf"
+		     : fmt == chars_format::scientific ? "%.*Le"
+		     : fmt == chars_format::general ? "%.*Lg"
+		     : fmt == chars_format::hex ? "%.*La"
+		     : nullptr);
+		unsigned output_length = snprintf(nullptr, 0, printf_specifier,
+						  precision, value);
+
+		char printf_buffer[output_length+1];
+		snprintf(printf_buffer, output_length+1, printf_specifier,
+			 precision, value);
+
+		char to_chars_buffer[output_length];
+		auto result = to_chars(to_chars_buffer,
+				       to_chars_buffer+output_length,
+				       value, fmt, precision);
+		VERIFY( result.ec == errc{} );
+		VERIFY( !memcmp(printf_buffer, to_chars_buffer, output_length) );
+
+		result = to_chars(to_chars_buffer,
+				  to_chars_buffer+output_length-1,
+				  value, fmt, precision);
+		VERIFY( result.ec == errc::value_too_large );
+	      }
+
+	    // Verify that the nearby values have a different shortest form.
+	    char to_chars_buffer[50000];
+	    auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer),
+				   value, fmt);
+	    VERIFY( result.ec == errc{} );
+	    *result.ptr = '\0';
+	    char nearby_buffer[50000];
+	      {
+		const long double smaller = nextdownl(value);
+		result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				  smaller, fmt);
+		VERIFY( result.ec == errc{} );
+		*result.ptr = '\0';
+		VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	      }
+
+	      {
+		long double larger = nextupl(value);
+		result = to_chars(begin(nearby_buffer), end(nearby_buffer),
+				  larger, fmt);
+		VERIFY( result.ec == errc{} );
+		*result.ptr = '\0';
+		VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
+	      }
+	  }
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
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");


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2020-12-17 14:43 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-17 14:43 [gcc(refs/users/ppalka/heads/libstdcxx-floating-to_chars)] libstdc++: Add floating-point std::to_chars implementation Patrick Palka
  -- strict thread matches above, loose matches on Subject: below --
2020-11-18 14:53 Patrick Palka
2020-11-18 14:22 Patrick Palka
2020-07-22 16:00 Patrick Palka
2020-07-22  3:10 Patrick Palka
2020-07-22  2:49 Patrick Palka
2020-07-20 14:38 Patrick Palka
2020-07-20 14:27 Patrick Palka
2020-07-18  1:43 Patrick Palka
2020-07-17  4:27 Patrick Palka
2020-07-15 19:01 Patrick Palka
2020-07-14 19:45 Patrick Palka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).