From: Jonathan Wakely <jwakely@redhat.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH 3/4] libstdc++: Add floating-point std::to_chars implementation
Date: Mon, 20 Jul 2020 13:31:23 +0100 [thread overview]
Message-ID: <20200720123123.GA3215@redhat.com> (raw)
In-Reply-To: <alpine.DEB.2.23.453.2007192126500.1521071@idea>
On 19/07/20 23:37 -0400, Patrick Palka via Libstdc++ wrote:
>On Fri, 17 Jul 2020, Patrick Palka wrote:
>
>> On Fri, 17 Jul 2020, Patrick Palka wrote:
>>
>> > On Wed, 15 Jul 2020, Patrick Palka wrote:
>> >
>> > > On Tue, 14 Jul 2020, Patrick Palka wrote:
>> > >
>> > > > 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.
>> > >
>> > > Here is v2 of this patch, which fixes a build failure on i386 due to
>> > > __int128 being unavailable, by refactoring the long double binary format
>> > > selection to avoid referring to __int128 when it doesn't exist. The
>> > > patch also makes the hex formatting for 80-bit long double use uint64_t
>> > > instead of __int128 since the mantissa has exactly 64 bits in this case.
>> >
>> > Here's v3 which just makes some minor stylistic adjustments, and most
>> > notably replaces the use of _GLIBCXX_DEBUG with _GLIBCXX_ASSERTIONS
>> > since we just want to enable __glibcxx_assert and not all of debug mode.
>>
>> Here's v4, which should now correctly support using <charconv> with
>> -mlong-double-64 on targets with a large default long double type.
>> This is done by defining the long double to_chars overloads as inline
>> wrappers around the double overloads within <charconv> whenever
>> __DBL_MANT_DIG__ equals __LDBL_MANT_DIG__.
>
>>
>> -- >8 --
>>
>> Subject: [PATCH 3/4] 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.
>> ---
>> libstdc++-v3/acinclude.m4 | 2 +-
>> libstdc++-v3/config/abi/pre/gnu.ver | 12 +
>> libstdc++-v3/configure | 2 +-
>> libstdc++-v3/include/std/charconv | 43 +
>> 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, 1774 insertions(+), 5 deletions(-)
>> create mode 100644 libstdc++-v3/src/c++17/floating_to_chars.cc
>> create mode 100644 libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
>>
>> 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..bd59924f7e7 100644
>> --- a/libstdc++-v3/include/std/charconv
>> +++ b/libstdc++-v3/include/std/charconv
>> @@ -688,6 +688,49 @@ 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 has the same binary format as 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
>
>Hmm, I think this approach for supporting -mlong-double-64 might
>introduce an ODR violation because each long double to_chars overload
>could potentially have two different definitions available in a program,
>one out-of-line in floating_to_chars.cc (compiled without
>-mlong-double-64) and another inline in <charconv> (compiled with
>-mlong-double-64)..
But they have different mangled names, so there's no ODR violation.
The 64-bit long double is mangled as 'e' and the 128-bit long double
is mangled as __float128. You *will* get an ODR violation on targets
where there's no -mlong-double-64 switch, where double and long double
are always the same representation.
What I'm doing for std::from_chars is adding this in the new
src/c++17/floating_from_chars.cc file:
#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
#pragma GCC diagnostic ignored "-Wattribute-alias"
extern "C" from_chars_result _ZSt10from_charsPKcS0_ReSt12chars_format(double)
__attribute__((alias ("_ZSt10from_charsPKcS0_RdSt12chars_format")));
#endif
This just defines the _ZSt10from_charsPKcS0_ReSt12chars_format symbol
(i.e. from_chars for 64-bit long double) as an alias of
_ZSt10from_charsPKcS0_RdSt12chars_format (i.e. from_chars for 64-bit
double).
next prev parent reply other threads:[~2020-07-20 12:31 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-07-14 19:41 [PATCH 1/4] libstdc++: Import parts of the Ryu library Patrick Palka
2020-07-14 19:41 ` [PATCH 2/4] libstdc++: Apply modifications to our local copy of Ryu Patrick Palka
2020-07-15 18:12 ` Patrick Palka
2020-12-17 14:35 ` Jonathan Wakely
2020-07-14 19:41 ` [PATCH 3/4] libstdc++: Add floating-point std::to_chars implementation Patrick Palka
2020-07-15 18:21 ` Patrick Palka
2020-07-17 4:23 ` Patrick Palka
2020-07-17 16:24 ` Patrick Palka
2020-07-20 3:37 ` Patrick Palka
2020-07-20 12:31 ` Jonathan Wakely [this message]
2020-07-20 12:53 ` Patrick Palka
2020-07-20 14:13 ` Jonathan Wakely
2020-07-20 14:46 ` Patrick Palka
2020-07-22 15:56 ` Patrick Palka
2020-08-19 21:57 ` Patrick Palka
2020-12-17 14:32 ` Jonathan Wakely
2020-12-18 4:13 ` Patrick Palka
2020-12-18 13:24 ` Christophe Lyon
2020-12-18 14:58 ` Jonathan Wakely
2020-12-18 15:00 ` Patrick Palka
2020-12-18 16:52 ` Christophe Lyon
2020-12-18 17:03 ` Patrick Palka
2020-12-18 18:28 ` Christophe Lyon
2020-12-20 21:44 ` Maciej W. Rozycki
2020-12-21 17:06 ` Patrick Palka
2020-12-21 23:09 ` Maciej W. Rozycki
2020-07-14 19:41 ` [PATCH 4/4] libstdc++: Import MSVC floating-point std::to_chars testcases Patrick Palka
2020-07-14 19:49 ` Patrick Palka
2020-12-17 14:37 ` Jonathan Wakely
2020-07-14 19:46 ` [PATCH 1/4] libstdc++: Import parts of the Ryu library Patrick Palka
2020-07-25 11:44 ` Václav Haisman
2020-07-26 13:09 ` Patrick Palka
2020-12-17 14:34 ` Jonathan Wakely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200720123123.GA3215@redhat.com \
--to=jwakely@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=libstdc++@gcc.gnu.org \
--cc=ppalka@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).