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