| // std::from_chars implementation for floating-point types -*- C++ -*- | |
| // Copyright (C) 2020-2026 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/>. | |
| // | |
| // ISO C++ 14882:2017 | |
| // 23.2.9 Primitive numeric input conversion [utility.from.chars] | |
| // | |
| // Prefer to use std::pmr::string if possible, which requires the cxx11 ABI. | |
| #define _GLIBCXX_USE_CXX11_ABI 1 | |
| #include <algorithm> | |
| #include <array> | |
| #include <charconv> | |
| #include <bit> | |
| #include <iterator> | |
| #include <limits> | |
| #include <string> | |
| #include <memory_resource> | |
| #include <cfenv> | |
| #include <cfloat> | |
| #include <cmath> | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <locale.h> | |
| #if _GLIBCXX_HAVE_XLOCALE_H | |
| # include <xlocale.h> | |
| #endif | |
| #if _GLIBCXX_HAVE_USELOCALE | |
| // FIXME: This should be reimplemented so it doesn't use strtod and newlocale. | |
| // That will avoid the need for any memory allocation, meaning that the | |
| // non-conforming errc::not_enough_memory result cannot happen. | |
| # define USE_STRTOD_FOR_FROM_CHARS 1 | |
| #endif | |
| #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| #ifndef __LONG_DOUBLE_IBM128__ | |
| #error "floating_from_chars.cc must be compiled with -mabi=ibmlongdouble" | |
| #endif | |
| // strtold for __ieee128 | |
| extern "C" __ieee128 __strtoieee128(const char*, char**); | |
| #elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \ | |
| && defined(__GLIBC_PREREQ) && defined(USE_STRTOD_FOR_FROM_CHARS) | |
| #define USE_STRTOF128_FOR_FROM_CHARS 1 | |
| extern "C" _Float128 __strtof128(const char*, char**) | |
| __asm ("strtof128") | |
| #ifndef _GLIBCXX_HAVE_FLOAT128_MATH | |
| __attribute__((__weak__)) | |
| #endif | |
| ; | |
| #endif | |
| #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \ | |
| && __SIZE_WIDTH__ >= 32 | |
| # define USE_LIB_FAST_FLOAT 1 | |
| #endif | |
| #if USE_LIB_FAST_FLOAT | |
| # define FASTFLOAT_DEBUG_ASSERT __glibcxx_assert | |
| namespace | |
| { | |
| # include "fast_float/fast_float.h" | |
| namespace fast_float | |
| { | |
| // Wrappers around float for std::{,b}float16_t promoted to float. | |
| struct floating_type_float16_t | |
| { | |
| float* x; | |
| uint16_t bits; | |
| }; | |
| struct floating_type_bfloat16_t | |
| { | |
| float* x; | |
| uint16_t bits; | |
| }; | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_float16_t>::mantissa_explicit_bits() | |
| { return 10; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_bfloat16_t>::mantissa_explicit_bits() | |
| { return 7; } | |
| // 10 bits of stored mantissa, pow(5,q) <= 0x4p+10 implies q <= 5 | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_float16_t>::max_exponent_round_to_even() | |
| { return 5; } | |
| // 7 bits of stored mantissa, pow(5,q) <= 0x4p+7 implies q <= 3 | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_bfloat16_t>::max_exponent_round_to_even() | |
| { return 3; } | |
| // 10 bits of stored mantissa, pow(5,-q) < 0x1p+64 / 0x1p+11 implies q >= -22 | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_float16_t>::min_exponent_round_to_even() | |
| { return -22; } | |
| // 7 bits of stored mantissa, pow(5,-q) < 0x1p+64 / 0x1p+8 implies q >= -24 | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_bfloat16_t>::min_exponent_round_to_even() | |
| { return -24; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_float16_t>::minimum_exponent() | |
| { return -15; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_bfloat16_t>::minimum_exponent() | |
| { return -127; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_float16_t>::infinite_power() | |
| { return 0x1F; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_bfloat16_t>::infinite_power() | |
| { return 0xFF; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_float16_t>::sign_index() | |
| { return 15; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_bfloat16_t>::sign_index() | |
| { return 15; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_float16_t>::largest_power_of_ten() | |
| { return 4; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_bfloat16_t>::largest_power_of_ten() | |
| { return 38; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_float16_t>::smallest_power_of_ten() | |
| { return -27; } | |
| template<> | |
| constexpr int | |
| binary_format<floating_type_bfloat16_t>::smallest_power_of_ten() | |
| { return -60; } | |
| template<> | |
| constexpr size_t | |
| binary_format<floating_type_float16_t>::max_digits() | |
| { return 22; } | |
| template<> | |
| constexpr size_t | |
| binary_format<floating_type_bfloat16_t>::max_digits() | |
| { return 98; } | |
| // negative_digit_comp converts adjusted_mantissa to the (originally only) | |
| // floating type and immediately back with slight tweaks (e.g. explicit | |
| // leading bit instead of implicit for normals). | |
| // Avoid going through the floating point type. | |
| template<> | |
| fastfloat_really_inline void | |
| to_float<floating_type_float16_t>(bool negative, adjusted_mantissa am, | |
| floating_type_float16_t &value) | |
| { | |
| constexpr int mantissa_bits | |
| = binary_format<floating_type_float16_t>::mantissa_explicit_bits(); | |
| value.bits = (am.mantissa | |
| | (uint16_t(am.power2) << mantissa_bits) | |
| | (negative ? 0x8000 : 0)); | |
| } | |
| template<> | |
| fastfloat_really_inline void | |
| to_float<floating_type_bfloat16_t>(bool negative, adjusted_mantissa am, | |
| floating_type_bfloat16_t &value) | |
| { | |
| constexpr int mantissa_bits | |
| = binary_format<floating_type_bfloat16_t>::mantissa_explicit_bits(); | |
| value.bits = (am.mantissa | |
| | (uint16_t(am.power2) << mantissa_bits) | |
| | (negative ? 0x8000 : 0)); | |
| } | |
| template <> | |
| fastfloat_really_inline adjusted_mantissa | |
| to_extended<floating_type_float16_t>(floating_type_float16_t value) noexcept | |
| { | |
| adjusted_mantissa am; | |
| constexpr int mantissa_bits | |
| = binary_format<floating_type_float16_t>::mantissa_explicit_bits(); | |
| int32_t bias | |
| = (mantissa_bits | |
| - binary_format<floating_type_float16_t>::minimum_exponent()); | |
| constexpr uint16_t exponent_mask = 0x7C00; | |
| constexpr uint16_t mantissa_mask = 0x03FF; | |
| constexpr uint16_t hidden_bit_mask = 0x0400; | |
| if ((value.bits & exponent_mask) == 0) { | |
| // denormal | |
| am.power2 = 1 - bias; | |
| am.mantissa = value.bits & mantissa_mask; | |
| } else { | |
| // normal | |
| am.power2 = int32_t((value.bits & exponent_mask) >> mantissa_bits); | |
| am.power2 -= bias; | |
| am.mantissa = (value.bits & mantissa_mask) | hidden_bit_mask; | |
| } | |
| return am; | |
| } | |
| template <> | |
| fastfloat_really_inline adjusted_mantissa | |
| to_extended<floating_type_bfloat16_t>(floating_type_bfloat16_t value) noexcept | |
| { | |
| adjusted_mantissa am; | |
| constexpr int mantissa_bits | |
| = binary_format<floating_type_bfloat16_t>::mantissa_explicit_bits(); | |
| int32_t bias | |
| = (mantissa_bits | |
| - binary_format<floating_type_bfloat16_t>::minimum_exponent()); | |
| constexpr uint16_t exponent_mask = 0x7F80; | |
| constexpr uint16_t mantissa_mask = 0x007F; | |
| constexpr uint16_t hidden_bit_mask = 0x0080; | |
| if ((value.bits & exponent_mask) == 0) { | |
| // denormal | |
| am.power2 = 1 - bias; | |
| am.mantissa = value.bits & mantissa_mask; | |
| } else { | |
| // normal | |
| am.power2 = int32_t((value.bits & exponent_mask) >> mantissa_bits); | |
| am.power2 -= bias; | |
| am.mantissa = (value.bits & mantissa_mask) | hidden_bit_mask; | |
| } | |
| return am; | |
| } | |
| // Like fast_float.h from_chars_advanced, but for 16-bit float. | |
| template<typename T> | |
| from_chars_result | |
| from_chars_16(const char* first, const char* last, T &value, | |
| chars_format fmt) noexcept | |
| { | |
| parse_options options{fmt}; | |
| from_chars_result answer; | |
| if (first == last) | |
| { | |
| answer.ec = std::errc::invalid_argument; | |
| answer.ptr = first; | |
| return answer; | |
| } | |
| parsed_number_string pns = parse_number_string(first, last, options); | |
| if (!pns.valid) | |
| return detail::parse_infnan(first, last, *value.x); | |
| answer.ec = std::errc(); | |
| answer.ptr = pns.lastmatch; | |
| adjusted_mantissa am | |
| = compute_float<binary_format<T>>(pns.exponent, pns.mantissa); | |
| if (pns.too_many_digits && am.power2 >= 0) | |
| { | |
| if (am != compute_float<binary_format<T>>(pns.exponent, | |
| pns.mantissa + 1)) | |
| am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa); | |
| } | |
| // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) | |
| // and we have an invalid power (am.power2 < 0), | |
| // then we need to go the long way around again. This is very uncommon. | |
| if (am.power2 < 0) | |
| am = digit_comp<T>(pns, am); | |
| if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) | |
| || am.power2 == binary_format<T>::infinite_power()) | |
| { | |
| // In case of over/underflow, return result_out_of_range and don't | |
| // modify value, as per [charconv.from.chars]/1. Note that LWG 3081 wants | |
| // to modify value in this case too. | |
| answer.ec = std::errc::result_out_of_range; | |
| return answer; | |
| } | |
| // Transform the {,b}float16_t to float32_t before to_float. | |
| if constexpr (std::is_same_v<T, floating_type_float16_t>) | |
| { | |
| if (am.power2 == 0) | |
| { | |
| if (am.mantissa) | |
| { | |
| int n = (std::numeric_limits<unsigned int>::digits | |
| - __builtin_clz (am.mantissa)) - 1; | |
| am.mantissa &= ~(static_cast<decltype(am.mantissa)>(1) << n); | |
| am.mantissa <<= (binary_format<float>::mantissa_explicit_bits() | |
| - n); | |
| am.power2 = n + 0x67; | |
| } | |
| } | |
| else | |
| { | |
| am.mantissa <<= 13; | |
| am.power2 += 0x70; | |
| } | |
| } | |
| else | |
| am.mantissa <<= 16; | |
| to_float(pns.negative, am, *value.x); | |
| return answer; | |
| } | |
| } // fast_float | |
| } // anon namespace | |
| #endif | |
| namespace std _GLIBCXX_VISIBILITY(default) | |
| { | |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION | |
| namespace | |
| { | |
| #if USE_STRTOD_FOR_FROM_CHARS | |
| // A memory resource with a static buffer that can be used for small | |
| // allocations. At most one allocation using the freestore can be done | |
| // if the static buffer is insufficient. The callers below only require | |
| // a single allocation, so there's no need for anything more complex. | |
| struct buffer_resource : pmr::memory_resource | |
| { | |
| ~buffer_resource() { if (m_ptr) operator delete(m_ptr, m_bytes); } | |
| void* | |
| do_allocate(size_t bytes, size_t alignment [[maybe_unused]]) override | |
| { | |
| // Allocate from the buffer if it will fit. | |
| if (m_bytes < sizeof(m_buf) && (m_bytes + bytes) <= sizeof(m_buf)) | |
| return m_buf + std::__exchange(m_bytes, m_bytes + bytes); | |
| __glibcxx_assert(m_ptr == nullptr); | |
| m_ptr = operator new(bytes); | |
| m_bytes = bytes; | |
| return m_ptr; | |
| } | |
| void | |
| do_deallocate(void*, size_t, size_t) noexcept override | |
| { /* like pmr::monotonic_buffer_resource, do nothing here */ } | |
| bool | |
| do_is_equal(const pmr::memory_resource& other) const noexcept override | |
| { return &other == this; } | |
| static constexpr int guaranteed_capacity() { return sizeof(m_buf); } | |
| private: | |
| char m_buf[512]; | |
| size_t m_bytes = 0; | |
| void* m_ptr = nullptr; | |
| }; | |
| #if _GLIBCXX_USE_CXX11_ABI | |
| using buffered_string = std::pmr::string; | |
| #else | |
| using buffered_string = std::string; | |
| #endif | |
| inline bool valid_fmt(chars_format fmt) | |
| { | |
| return fmt != chars_format{} | |
| && ((fmt & chars_format::general) == fmt | |
| || (fmt & chars_format::hex) == fmt); | |
| } | |
| constexpr char hex_digits[] = "abcdefABCDEF0123456789"; | |
| constexpr auto dec_digits = hex_digits + 12; | |
| // Find initial portion of [first, last) containing a floating-point number. | |
| // The string `digits` is either `dec_digits` or `hex_digits` | |
| // and `exp` is "eE", "pP" or NULL. | |
| const char* | |
| find_end_of_float(const char* first, const char* last, const char* digits, | |
| const char *exp) | |
| { | |
| while (first < last && strchr(digits, *first) != nullptr) | |
| ++first; | |
| if (first < last && *first == '.') | |
| { | |
| ++first; | |
| while (first < last && strchr(digits, *first)) | |
| ++first; | |
| } | |
| if (first < last && exp != nullptr && (*first == exp[0] || *first == exp[1])) | |
| { | |
| ++first; | |
| if (first < last && (*first == '-' || *first == '+')) | |
| ++first; | |
| while (first < last && strchr(dec_digits, *first) != nullptr) | |
| ++first; | |
| } | |
| return first; | |
| } | |
| // Determine the prefix of [first, last) that matches the pattern | |
| // corresponding to `fmt`. | |
| // Returns a NTBS containing the pattern, using `buf` to allocate | |
| // additional storage if needed. | |
| // Returns a nullptr if a valid pattern is not present. | |
| const char* | |
| pattern(const char* const first, const char* last, | |
| chars_format& fmt, buffered_string& buf) | |
| { | |
| // fmt has the value of one of the enumerators of chars_format. | |
| __glibcxx_assert(valid_fmt(fmt)); | |
| string_view res; | |
| if (first == last || *first == '+') [[unlikely]] | |
| return nullptr; | |
| const int neg = (*first == '-'); | |
| if (std::memchr("iInN", (unsigned char)first[neg], 4)) | |
| { | |
| ptrdiff_t len = last - first; | |
| if (len < (3 + neg)) | |
| return nullptr; | |
| // possible infinity or NaN, let strtod decide | |
| if (first[neg] == 'i' || first[neg] == 'I') | |
| { | |
| // Need at most 9 chars for "-INFINITY", ignore anything after it. | |
| len = std::min(len, ptrdiff_t(neg + 8)); | |
| } | |
| else if (len > (neg + 3) && first[neg + 3] == '(') | |
| { | |
| // Look for end of "NAN(n-char-sequence)" | |
| if (void* p = std::memchr(const_cast<char*>(first)+4, ')', len-4)) | |
| len = static_cast<char*>(p) + 1 - first; | |
| #ifndef __cpp_exceptions | |
| if (len > buffer_resource::guaranteed_capacity()) | |
| { | |
| // The character sequence is too large for the buffer. | |
| // Allocation failure could terminate the process, | |
| // so just return an error via the fmt parameter. | |
| fmt = chars_format{}; | |
| return nullptr; | |
| } | |
| #endif | |
| } | |
| else // Only need 4 chars for "-NAN" | |
| len = neg + 3; | |
| buf.assign(first, 0, len); | |
| // prevent make_result correcting for "0x" | |
| fmt = chars_format::general; | |
| return buf.c_str(); | |
| } | |
| const char* digits; | |
| char* ptr; | |
| // Assign [first,last) to a std::string to get a NTBS that can be used | |
| // with strspn, strtod etc. | |
| // If the string would be longer than the fixed buffer inside the | |
| // buffer_resource type use find_end_of_float to try to reduce how | |
| // much memory is needed, to reduce the chance of std::bad_alloc. | |
| if (fmt == chars_format::hex) | |
| { | |
| digits = hex_digits; | |
| if ((last - first + 2) > buffer_resource::guaranteed_capacity()) | |
| { | |
| last = find_end_of_float(first + neg, last, digits, "pP"); | |
| #ifndef __cpp_exceptions | |
| if ((last - first + 2) > buffer_resource::guaranteed_capacity()) | |
| { | |
| // The character sequence is still too large for the buffer. | |
| // Allocation failure could terminate the process, | |
| // so just return an error via the fmt parameter. | |
| fmt = chars_format{}; | |
| return nullptr; | |
| } | |
| #endif | |
| } | |
| buf = "-0x" + !neg; | |
| buf.append(first + neg, last); | |
| ptr = buf.data() + neg + 2; | |
| } | |
| else | |
| { | |
| digits = dec_digits; | |
| if ((last - first) > buffer_resource::guaranteed_capacity()) | |
| { | |
| last = find_end_of_float(first + neg, last, digits, | |
| fmt == chars_format::fixed ? nullptr : "eE"); | |
| #ifndef __cpp_exceptions | |
| if ((last - first) > buffer_resource::guaranteed_capacity()) | |
| { | |
| // The character sequence is still too large for the buffer. | |
| // Allocation failure could terminate the process, | |
| // so just return an error via the fmt parameter. | |
| fmt = chars_format{}; | |
| return nullptr; | |
| } | |
| #endif | |
| } | |
| buf.assign(first, last); | |
| ptr = buf.data() + neg; | |
| } | |
| // "A non-empty sequence of decimal digits" or | |
| // "A non-empty sequence of hexadecimal digits" | |
| size_t len = std::strspn(ptr, digits); | |
| // "possibly containing a radix character," | |
| if (ptr[len] == '.') | |
| { | |
| const size_t len2 = std::strspn(ptr + len + 1, digits); | |
| if (len + len2) | |
| ptr += len + 1 + len2; | |
| else | |
| return nullptr; | |
| } | |
| else if (len == 0) [[unlikely]] | |
| return nullptr; | |
| else | |
| ptr += len; | |
| if (fmt == chars_format::fixed) | |
| { | |
| // Truncate the string to stop strtod parsing past this point. | |
| *ptr = '\0'; | |
| } | |
| else if (fmt == chars_format::scientific) | |
| { | |
| // Check for required exponent part which starts with 'e' or 'E' | |
| if (*ptr != 'e' && *ptr != 'E') | |
| return nullptr; | |
| // then an optional plus or minus sign | |
| const int sign = (ptr[1] == '-' || ptr[1] == '+'); | |
| // then a nonempty sequence of decimal digits | |
| if (!std::memchr(dec_digits, (unsigned char)ptr[1+sign], 10)) | |
| return nullptr; | |
| } | |
| else if (fmt == chars_format::general) | |
| { | |
| if (*ptr == 'x' || *ptr == 'X') | |
| *ptr = '\0'; | |
| } | |
| return buf.c_str(); | |
| } | |
| // RAII type to change and restore the locale. | |
| struct auto_locale | |
| { | |
| #if _GLIBCXX_HAVE_USELOCALE | |
| // When we have uselocale we can change the current thread's locale. | |
| const locale_t loc; | |
| locale_t orig; | |
| auto_locale() | |
| : loc(::newlocale(LC_ALL_MASK, "C", (locale_t)0)) | |
| { | |
| if (loc) | |
| orig = ::uselocale(loc); | |
| else | |
| ec = errc{errno}; | |
| } | |
| ~auto_locale() | |
| { | |
| if (loc) | |
| { | |
| ::uselocale(orig); | |
| ::freelocale(loc); | |
| } | |
| } | |
| #else | |
| // Otherwise, we can't change the locale and so strtod can't be used. | |
| auto_locale() = delete; | |
| #endif | |
| explicit operator bool() const noexcept { return ec == errc{}; } | |
| errc ec{}; | |
| auto_locale(const auto_locale&) = delete; | |
| auto_locale& operator=(const auto_locale&) = delete; | |
| }; | |
| // RAII type to change and restore the floating-point environment. | |
| struct auto_ferounding | |
| { | |
| #if _GLIBCXX_USE_C99_FENV_TR1 && defined(FE_TONEAREST) | |
| const int rounding = std::fegetround(); | |
| auto_ferounding() | |
| { | |
| if (rounding != FE_TONEAREST) | |
| std::fesetround(FE_TONEAREST); | |
| } | |
| ~auto_ferounding() | |
| { | |
| if (rounding != FE_TONEAREST) | |
| std::fesetround(rounding); | |
| } | |
| #else | |
| auto_ferounding() = default; | |
| #endif | |
| auto_ferounding(const auto_ferounding&) = delete; | |
| auto_ferounding& operator=(const auto_ferounding&) = delete; | |
| }; | |
| // Convert the NTBS `str` to a floating-point value of type `T`. | |
| // If `str` cannot be converted, `value` is unchanged and `0` is returned. | |
| // Otherwise, let N be the number of characters consumed from `str`. | |
| // On success `value` is set to the converted value and N is returned. | |
| // If the converted value is out of range, `value` is unchanged and | |
| // -N is returned. | |
| template<typename T> | |
| ptrdiff_t | |
| from_chars_impl(const char* str, T& value, errc& ec) noexcept | |
| { | |
| auto_locale loc; | |
| if (loc) | |
| { | |
| auto_ferounding rounding; | |
| const int save_errno = errno; | |
| errno = 0; | |
| char* endptr; | |
| T tmpval; | |
| #if _GLIBCXX_USE_C99_STDLIB | |
| if constexpr (is_same_v<T, float>) | |
| tmpval = std::strtof(str, &endptr); | |
| else if constexpr (is_same_v<T, double>) | |
| tmpval = std::strtod(str, &endptr); | |
| else if constexpr (is_same_v<T, long double>) | |
| tmpval = std::strtold(str, &endptr); | |
| # ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| else if constexpr (is_same_v<T, __ieee128>) | |
| tmpval = __strtoieee128(str, &endptr); | |
| # elif defined(USE_STRTOF128_FOR_FROM_CHARS) | |
| else if constexpr (is_same_v<T, _Float128>) | |
| { | |
| #ifndef _GLIBCXX_HAVE_FLOAT128_MATH | |
| if (&__strtof128 == nullptr) | |
| tmpval = _Float128(std::strtold(str, &endptr)); | |
| else | |
| #endif | |
| tmpval = __strtof128(str, &endptr); | |
| } | |
| # endif | |
| #else | |
| tmpval = std::strtod(str, &endptr); | |
| #endif | |
| const int conv_errno = std::__exchange(errno, save_errno); | |
| const ptrdiff_t n = endptr - str; | |
| if (conv_errno == ERANGE) [[unlikely]] | |
| { | |
| if (__builtin_isinf(tmpval)) // overflow | |
| ec = errc::result_out_of_range; | |
| else if (tmpval == 0) // underflow (LWG 3081 wants to set value = tmpval here) | |
| ec = errc::result_out_of_range; | |
| else // denormal value | |
| { | |
| value = tmpval; | |
| ec = errc(); | |
| } | |
| } | |
| else if (n) | |
| { | |
| value = tmpval; | |
| ec = errc(); | |
| } | |
| return n; | |
| } | |
| else | |
| ec = loc.ec; | |
| return 0; | |
| } | |
| inline from_chars_result | |
| make_result(const char* str, ptrdiff_t n, chars_format fmt, errc ec) noexcept | |
| { | |
| from_chars_result result = { str, ec }; | |
| if (n != 0) | |
| { | |
| if (fmt == chars_format::hex) | |
| n -= 2; // correct for the "0x" inserted into the pattern | |
| result.ptr += n; | |
| } | |
| else if (fmt == chars_format{}) [[unlikely]] | |
| { | |
| // FIXME: the standard does not allow this result. | |
| ec = errc::not_enough_memory; | |
| } | |
| return result; | |
| } | |
| #if ! _GLIBCXX_USE_CXX11_ABI | |
| inline bool | |
| reserve_string(std::string& s) noexcept | |
| { | |
| __try | |
| { | |
| s.reserve(buffer_resource::guaranteed_capacity()); | |
| } | |
| __catch (const std::bad_alloc&) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| #endif | |
| template<typename T> | |
| from_chars_result | |
| from_chars_strtod(const char* first, const char* last, T& value, | |
| chars_format fmt) noexcept | |
| { | |
| errc ec = errc::invalid_argument; | |
| #if _GLIBCXX_USE_CXX11_ABI | |
| buffer_resource mr; | |
| pmr::string buf(&mr); | |
| #else | |
| string buf; | |
| if (!reserve_string(buf)) | |
| return make_result(first, 0, {}, ec); | |
| #endif | |
| size_t len = 0; | |
| __try | |
| { | |
| if (const char* pat = pattern(first, last, fmt, buf)) [[likely]] | |
| len = from_chars_impl(pat, value, ec); | |
| } | |
| __catch (const std::bad_alloc&) | |
| { | |
| fmt = chars_format{}; | |
| } | |
| return make_result(first, len, fmt, ec); | |
| } | |
| #endif // USE_STRTOD_FOR_FROM_CHARS | |
| #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 | |
| // Return true iff [FIRST,LAST) begins with PREFIX, ignoring case. | |
| // PREFIX is assumed to not contain any uppercase letters. | |
| bool | |
| starts_with_ci(const char* first, const char* last, string_view prefix) | |
| { | |
| __glibcxx_requires_valid_range(first, last); | |
| // A lookup table that maps uppercase letters to lowercase and | |
| // is otherwise the identity mapping. | |
| static constexpr auto upper_to_lower_table = [] { | |
| constexpr unsigned char lower_letters[27] = "abcdefghijklmnopqrstuvwxyz"; | |
| constexpr unsigned char upper_letters[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
| std::array<unsigned char, (1u << __CHAR_BIT__)> table = {}; | |
| for (unsigned i = 0; i < table.size(); ++i) | |
| table[i] = i; | |
| for (unsigned i = 0; i < 26; ++i) | |
| table[upper_letters[i]] = lower_letters[i]; | |
| return table; | |
| }(); | |
| if (last - first < static_cast<ptrdiff_t>(prefix.length())) | |
| return false; | |
| for (const unsigned char pch : prefix) | |
| { | |
| // __glibcxx_assert(pch == upper_to_lower_table[pch]); | |
| const unsigned char ch = *first; | |
| if (ch != pch && upper_to_lower_table[ch] != pch) | |
| return false; | |
| ++first; | |
| } | |
| return true; | |
| } | |
| // An implementation of hexadecimal float parsing for binary32/64. | |
| template<typename T> | |
| from_chars_result | |
| __floating_from_chars_hex(const char* first, const char* last, T& value) | |
| { | |
| using uint_t = conditional_t<is_same_v<T, float>, uint32_t, | |
| conditional_t<is_same_v<T, double>, uint64_t, | |
| uint16_t>>; | |
| #if USE_LIB_FAST_FLOAT | |
| constexpr int mantissa_bits | |
| = fast_float::binary_format<T>::mantissa_explicit_bits(); | |
| constexpr int exponent_bits | |
| = is_same_v<T, double> ? 11 | |
| : is_same_v<T, fast_float::floating_type_float16_t> ? 5 : 8; | |
| #else | |
| constexpr int mantissa_bits = is_same_v<T, float> ? 23 : 52; | |
| constexpr int exponent_bits = is_same_v<T, float> ? 8 : 11; | |
| #endif | |
| constexpr int exponent_bias = (1 << (exponent_bits - 1)) - 1; | |
| __glibcxx_requires_valid_range(first, last); | |
| if (first == last) | |
| return {first, errc::invalid_argument}; | |
| // Consume the sign bit. | |
| const char* const orig_first = first; | |
| bool sign_bit = false; | |
| if (*first == '-') | |
| { | |
| sign_bit = true; | |
| ++first; | |
| } | |
| // Handle "inf", "infinity", "NaN" and variants thereof. | |
| if (first != last) | |
| if (*first == 'i' || *first == 'I' || *first == 'n' || *first == 'N') [[unlikely]] | |
| { | |
| if (starts_with_ci(first, last, "inf"sv)) | |
| { | |
| first += strlen("inf"); | |
| if (starts_with_ci(first, last, "inity"sv)) | |
| first += strlen("inity"); | |
| if constexpr (is_same_v<T, float> || is_same_v<T, double>) | |
| { | |
| uint_t result = 0; | |
| result |= sign_bit; | |
| result <<= exponent_bits; | |
| result |= (1ull << exponent_bits) - 1; | |
| result <<= mantissa_bits; | |
| memcpy(&value, &result, sizeof(result)); | |
| } | |
| else | |
| { | |
| // float +/-Inf. | |
| uint32_t result = 0x7F800000 | (sign_bit ? 0x80000000U : 0); | |
| memcpy(value.x, &result, sizeof(result)); | |
| } | |
| return {first, errc{}}; | |
| } | |
| else if (starts_with_ci(first, last, "nan")) | |
| { | |
| first += strlen("nan"); | |
| if (first != last && *first == '(') | |
| { | |
| // Tentatively consume the '(' as we look for an optional | |
| // n-char-sequence followed by a ')'. | |
| const char* const fallback_first = first; | |
| for (;;) | |
| { | |
| ++first; | |
| if (first == last) | |
| { | |
| first = fallback_first; | |
| break; | |
| } | |
| char ch = *first; | |
| if (ch == ')') | |
| { | |
| ++first; | |
| break; | |
| } | |
| else if (ch == '_' | |
| || __detail::__from_chars_alnum_to_val(ch) < 127) | |
| continue; | |
| else | |
| { | |
| first = fallback_first; | |
| break; | |
| } | |
| } | |
| } | |
| // We make the implementation-defined decision of ignoring the | |
| // sign bit and the n-char-sequence when assembling the NaN. | |
| if constexpr (is_same_v<T, float> || is_same_v<T, double>) | |
| { | |
| uint_t result = 0; | |
| result <<= exponent_bits; | |
| result |= (1ull << exponent_bits) - 1; | |
| result <<= mantissa_bits; | |
| result |= (1ull << (mantissa_bits - 1)) | 1; | |
| memcpy(&value, &result, sizeof(result)); | |
| } | |
| else | |
| { | |
| // float qNaN. | |
| uint32_t result = 0x7FC00001; | |
| memcpy(value.x, &result, sizeof(result)); | |
| } | |
| return {first, errc{}}; | |
| } | |
| } | |
| // Consume all insignificant leading zeros in the whole part of the | |
| // mantissa. | |
| bool seen_hexit = false; | |
| while (first != last && *first == '0') | |
| { | |
| seen_hexit = true; | |
| ++first; | |
| } | |
| // Now consume the rest of the written mantissa, populating MANTISSA with | |
| // the first MANTISSA_BITS+k significant bits of the written mantissa, where | |
| // 1 <= k <= 4 is the bit width of the leading significant written hexit. | |
| // | |
| // Examples: | |
| // After parsing "1.2f3", MANTISSA is 0x12f30000000000 (bit_width=52+1). | |
| // After parsing ".0000f0e", MANTISSA is 0xf0e00000000000 (bit_width=52+4). | |
| // After parsing ".1234567890abcd8", MANTISSA is 0x1234567890abcd (bit_width=52+1) | |
| // and MIDPOINT_BIT is true (and NONZERO_TAIL is false). | |
| uint_t mantissa = 0; | |
| int mantissa_idx = mantissa_bits; // The current bit index into MANTISSA | |
| // into which we'll write the next hexit. | |
| int exponent_adjustment = 0; // How much we'd have to adjust the written | |
| // exponent in order to represent the mantissa | |
| // in scientific form h.hhhhhhhhhhhhh. | |
| bool midpoint_bit = false; // Whether the MANTISSA_BITS+k+1 significant | |
| // bit is set in the written mantissa. | |
| bool nonzero_tail = false; // Whether some bit thereafter is set in the | |
| // written mantissa. | |
| bool seen_decimal_point = false; | |
| for (; first != last; ++first) | |
| { | |
| char ch = *first; | |
| if (ch == '.' && !seen_decimal_point) | |
| { | |
| seen_decimal_point = true; | |
| continue; | |
| } | |
| int hexit = __detail::__from_chars_alnum_to_val(ch); | |
| if (hexit >= 16) | |
| break; | |
| seen_hexit = true; | |
| if (!seen_decimal_point && mantissa != 0) | |
| exponent_adjustment += 4; | |
| else if (seen_decimal_point && mantissa == 0) | |
| { | |
| exponent_adjustment -= 4; | |
| if (hexit == 0x0) | |
| continue; | |
| } | |
| if (mantissa_idx >= 0) | |
| mantissa |= uint_t(hexit) << mantissa_idx; | |
| else if (mantissa_idx >= -4) | |
| { | |
| if constexpr (is_same_v<T, float> | |
| #if USE_LIB_FAST_FLOAT | |
| || is_same_v<T, | |
| fast_float::floating_type_bfloat16_t> | |
| #endif | |
| ) | |
| { | |
| __glibcxx_assert(mantissa_idx == -1); | |
| mantissa |= hexit >> 1; | |
| midpoint_bit = (hexit & 0b0001) != 0; | |
| } | |
| else if constexpr (is_same_v<T, double>) | |
| { | |
| __glibcxx_assert(mantissa_idx == -4); | |
| midpoint_bit = (hexit & 0b1000) != 0; | |
| nonzero_tail = (hexit & 0b0111) != 0; | |
| } | |
| else | |
| { | |
| __glibcxx_assert(mantissa_idx == -2); | |
| mantissa |= hexit >> 2; | |
| midpoint_bit = (hexit & 0b0010) != 0; | |
| nonzero_tail = (hexit & 0b0001) != 0; | |
| } | |
| } | |
| else | |
| nonzero_tail |= (hexit != 0x0); | |
| mantissa_idx -= 4; | |
| } | |
| if (mantissa != 0) | |
| __glibcxx_assert(__bit_width(mantissa) >= mantissa_bits + 1 | |
| && __bit_width(mantissa) <= mantissa_bits + 4); | |
| else | |
| __glibcxx_assert(!midpoint_bit && !nonzero_tail); | |
| if (!seen_hexit) | |
| // If we haven't seen any hexit at this point, the parse failed. | |
| return {orig_first, errc::invalid_argument}; | |
| // Parse the written exponent. | |
| int written_exponent = 0; | |
| if (first != last && (*first == 'p' || *first == 'P')) | |
| { | |
| // Tentatively consume the 'p' and try to parse a decimal number. | |
| const char* const fallback_first = first; | |
| ++first; | |
| if (first != last && *first == '+') | |
| ++first; | |
| from_chars_result fcr = from_chars(first, last, written_exponent, 10); | |
| if (fcr.ptr == first) | |
| // The parse failed, so undo consuming the 'p' and carry on as if the | |
| // exponent was omitted (i.e. is 0). | |
| first = fallback_first; | |
| else | |
| { | |
| first = fcr.ptr; | |
| if (mantissa != 0 && fcr.ec == errc::result_out_of_range) | |
| // Punt on very large exponents for now. FIXME | |
| return {first, errc::result_out_of_range}; | |
| } | |
| } | |
| int biased_exponent = written_exponent + exponent_bias; | |
| if (exponent_adjustment != 0) | |
| // The mantissa wasn't written in scientific form. Adjust the exponent | |
| // so that we may assume scientific form. | |
| // | |
| // Examples; | |
| // For input "a.bcp5", EXPONENT_ADJUSTMENT would be 0 since this | |
| // written mantissa is already in scientific form. | |
| // For input "ab.cp5", EXPONENT_ADJUSTMENT would be 4 since the | |
| // scientific form is "a.bcp9". | |
| // For input 0.0abcp5", EXPONENT_ADJUSTMENT would be -8 since the | |
| // scientific form is "a.bcp-3". | |
| biased_exponent += exponent_adjustment; | |
| // Shifts the mantissa to the right by AMOUNT while updating | |
| // BIASED_EXPONENT, MIDPOINT_BIT and NONZERO_TAIL accordingly. | |
| auto shift_mantissa = [&] (int amount) { | |
| __glibcxx_assert(amount >= 0); | |
| if (amount > mantissa_bits + 1) | |
| { | |
| // Shifting the mantissa by an amount greater than its precision. | |
| nonzero_tail |= midpoint_bit; | |
| nonzero_tail |= mantissa != 0; | |
| midpoint_bit = false; | |
| mantissa = 0; | |
| biased_exponent += amount; | |
| } | |
| else if (amount != 0) | |
| { | |
| nonzero_tail |= midpoint_bit; | |
| nonzero_tail |= (mantissa & ((1ull << (amount - 1)) - 1)) != 0; | |
| midpoint_bit = (mantissa & (1ull << (amount - 1))) != 0; | |
| mantissa >>= amount; | |
| biased_exponent += amount; | |
| } | |
| }; | |
| if (mantissa != 0) | |
| { | |
| // If the leading hexit is not '1', shift MANTISSA to make it so. | |
| // This normalizes input like "4.08p0" into "1.02p2". | |
| const unsigned leading_hexit = mantissa >> mantissa_bits; | |
| const int leading_hexit_width = __bit_width(leading_hexit); // FIXME: optimize? | |
| __glibcxx_assert(leading_hexit_width >= 1 && leading_hexit_width <= 4); | |
| shift_mantissa(leading_hexit_width - 1); | |
| // After this adjustment, we can assume the leading hexit is '1'. | |
| __glibcxx_assert((mantissa >> mantissa_bits) == 0x1); | |
| } | |
| if (biased_exponent <= 0) | |
| { | |
| // This number is too small to be represented as a normal number, so | |
| // try for a subnormal number by shifting the mantissa sufficiently. | |
| // We need to shift by 1 more than -BIASED_EXPONENT because the leading | |
| // mantissa bit is omitted in the representation of a normal number but | |
| // not in a subnormal number. | |
| shift_mantissa(-biased_exponent + 1); | |
| __glibcxx_assert(!(mantissa & (1ull << mantissa_bits))); | |
| __glibcxx_assert(biased_exponent == 1); | |
| biased_exponent = 0; | |
| } | |
| // Perform round-to-nearest, tie-to-even rounding according to | |
| // MIDPOINT_BIT and NONZERO_TAIL. | |
| if (midpoint_bit && (nonzero_tail || (mantissa % 2) != 0)) | |
| { | |
| // Rounding away from zero. | |
| ++mantissa; | |
| midpoint_bit = false; | |
| nonzero_tail = false; | |
| // Deal with a couple of corner cases after rounding. | |
| if (mantissa == (1ull << mantissa_bits)) | |
| { | |
| // We rounded the subnormal number 1.fffffffffffff...p-1023 | |
| // up to the normal number 1p-1022. | |
| __glibcxx_assert(biased_exponent == 0); | |
| ++biased_exponent; | |
| } | |
| else if (mantissa & (1ull << (mantissa_bits + 1))) | |
| { | |
| // We rounded the normal number 1.fffffffffffff8pN (with maximal | |
| // mantissa) up to to 1p(N+1). | |
| mantissa >>= 1; | |
| ++biased_exponent; | |
| } | |
| } | |
| else | |
| { | |
| // Rounding toward zero. | |
| if (mantissa == 0 && (midpoint_bit || nonzero_tail)) | |
| { | |
| // A nonzero number that rounds to zero is unrepresentable. | |
| __glibcxx_assert(biased_exponent == 0); | |
| return {first, errc::result_out_of_range}; | |
| } | |
| midpoint_bit = false; | |
| nonzero_tail = false; | |
| } | |
| if (mantissa != 0 && biased_exponent >= (1 << exponent_bits) - 1) | |
| // The exponent of this number is too large to be representable. | |
| return {first, errc::result_out_of_range}; | |
| uint_t result = 0; | |
| if (mantissa == 0) | |
| { | |
| // Assemble a (possibly signed) zero. | |
| if (sign_bit) | |
| result |= 1ull << (exponent_bits + mantissa_bits); | |
| } | |
| else | |
| { | |
| // Assemble a nonzero normal or subnormal value. | |
| result |= sign_bit; | |
| result <<= exponent_bits; | |
| result |= biased_exponent; | |
| result <<= mantissa_bits; | |
| result |= mantissa & ((1ull << mantissa_bits) - 1); | |
| // The implicit leading mantissa bit is set iff the number is normal. | |
| __glibcxx_assert(((mantissa & (1ull << mantissa_bits)) != 0) | |
| == (biased_exponent != 0)); | |
| } | |
| if constexpr (is_same_v<T, float> || is_same_v<T, double>) | |
| memcpy(&value, &result, sizeof(result)); | |
| #if USE_LIB_FAST_FLOAT | |
| else if constexpr (is_same_v<T, fast_float::floating_type_bfloat16_t>) | |
| { | |
| uint32_t res = uint32_t{result} << 16; | |
| memcpy(value.x, &res, sizeof(res)); | |
| } | |
| else | |
| { | |
| // Otherwise float16_t which needs to be converted to float32_t. | |
| uint32_t res; | |
| if ((result & 0x7FFF) == 0) | |
| res = uint32_t{result} << 16; // +/-0.0f16 | |
| else if ((result & 0x7C00) == 0) | |
| { // denormal | |
| unsigned n = (std::numeric_limits<unsigned int>::digits | |
| - __builtin_clz (result & 0x3FF) - 1); | |
| res = uint32_t{result} & 0x3FF & ~(uint32_t{1} << n); | |
| res <<= 23 - n; | |
| res |= (((uint32_t{n} + 0x67) << 23) | |
| | ((uint32_t{result} & 0x8000) << 16)); | |
| } | |
| else | |
| res = (((uint32_t{result} & 0x3FF) << 13) | |
| | ((((uint32_t{result} >> 10) & 0x1F) + 0x70) << 23) | |
| | ((uint32_t{result} & 0x8000) << 16)); | |
| memcpy(value.x, &res, sizeof(res)); | |
| } | |
| #endif | |
| return {first, errc{}}; | |
| } | |
| #endif // _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 | |
| } // namespace | |
| #if USE_LIB_FAST_FLOAT || USE_STRTOD_FOR_FROM_CHARS | |
| from_chars_result | |
| from_chars(const char* first, const char* last, float& value, | |
| chars_format fmt) noexcept | |
| { | |
| #if USE_LIB_FAST_FLOAT | |
| if (fmt == chars_format::hex) | |
| return __floating_from_chars_hex(first, last, value); | |
| else | |
| return fast_float::from_chars(first, last, value, fmt); | |
| #else | |
| return from_chars_strtod(first, last, value, fmt); | |
| #endif | |
| } | |
| from_chars_result | |
| from_chars(const char* first, const char* last, double& value, | |
| chars_format fmt) noexcept | |
| { | |
| #if USE_LIB_FAST_FLOAT | |
| if (fmt == chars_format::hex) | |
| return __floating_from_chars_hex(first, last, value); | |
| else | |
| return fast_float::from_chars(first, last, value, fmt); | |
| #else | |
| return from_chars_strtod(first, last, value, fmt); | |
| #endif | |
| } | |
| from_chars_result | |
| from_chars(const char* first, const char* last, long double& value, | |
| chars_format fmt) noexcept | |
| { | |
| #if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__ || !defined USE_STRTOD_FOR_FROM_CHARS | |
| // Either long double is the same as double, or we can't use strtold. | |
| // In the latter case, this might give an incorrect result (e.g. values | |
| // out of range of double give an error, even if they fit in long double). | |
| double dbl_value; | |
| from_chars_result result; | |
| if (fmt == chars_format::hex) | |
| result = __floating_from_chars_hex(first, last, dbl_value); | |
| else | |
| result = fast_float::from_chars(first, last, dbl_value, fmt); | |
| if (result.ec == errc{}) | |
| value = dbl_value; | |
| return result; | |
| #else | |
| return from_chars_strtod(first, last, value, fmt); | |
| #endif | |
| } | |
| #if USE_LIB_FAST_FLOAT | |
| // Entrypoints for 16-bit floats. | |
| [[gnu::cold]] from_chars_result | |
| __from_chars_float16_t(const char* first, const char* last, float& value, | |
| chars_format fmt) noexcept | |
| { | |
| struct fast_float::floating_type_float16_t val{ &value, 0 }; | |
| if (fmt == chars_format::hex) | |
| return __floating_from_chars_hex(first, last, val); | |
| else | |
| return fast_float::from_chars_16(first, last, val, fmt); | |
| } | |
| [[gnu::cold]] from_chars_result | |
| __from_chars_bfloat16_t(const char* first, const char* last, float& value, | |
| chars_format fmt) noexcept | |
| { | |
| struct fast_float::floating_type_bfloat16_t val{ &value, 0 }; | |
| if (fmt == chars_format::hex) | |
| return __floating_from_chars_hex(first, last, val); | |
| else | |
| return fast_float::from_chars_16(first, last, val, fmt); | |
| } | |
| #endif | |
| #ifdef _GLIBCXX_LONG_DOUBLE_COMPAT | |
| // Make std::from_chars for 64-bit long double an alias for the overload | |
| // for double. | |
| extern "C" from_chars_result | |
| _ZSt10from_charsPKcS0_ReSt12chars_format(const char* first, const char* last, | |
| long double& value, | |
| chars_format fmt) noexcept | |
| __attribute__((alias ("_ZSt10from_charsPKcS0_RdSt12chars_format"))); | |
| #endif | |
| #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| from_chars_result | |
| from_chars(const char* first, const char* last, __ieee128& value, | |
| chars_format fmt) noexcept | |
| { | |
| // fast_float doesn't support IEEE binary128 format, but we can use strtold. | |
| return from_chars_strtod(first, last, value, fmt); | |
| } | |
| extern "C" from_chars_result | |
| _ZSt10from_charsPKcS0_RDF128_St12chars_format(const char* first, | |
| const char* last, | |
| __ieee128& value, | |
| chars_format fmt) noexcept | |
| __attribute__((alias ("_ZSt10from_charsPKcS0_Ru9__ieee128St12chars_format"))); | |
| #elif defined(USE_STRTOF128_FOR_FROM_CHARS) | |
| // Overload for _Float128 is not defined inline in <charconv>, define it here. | |
| from_chars_result | |
| from_chars(const char* first, const char* last, _Float128& value, | |
| chars_format fmt) noexcept | |
| { | |
| // fast_float doesn't support IEEE binary128 format, but we can use strtold. | |
| return from_chars_strtod(first, last, value, fmt); | |
| } | |
| #endif | |
| #endif // USE_LIB_FAST_FLOAT || USE_STRTOD_FOR_FROM_CHARS | |
| _GLIBCXX_END_NAMESPACE_VERSION | |
| } // namespace std |