From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 57F5C385800E for ; Mon, 17 Jan 2022 10:34:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 57F5C385800E Received: from mail-yb1-f200.google.com (mail-yb1-f200.google.com [209.85.219.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-167-vHHPXlz_PUOC1KOseBTTIw-1; Mon, 17 Jan 2022 05:34:22 -0500 X-MC-Unique: vHHPXlz_PUOC1KOseBTTIw-1 Received: by mail-yb1-f200.google.com with SMTP id g7-20020a25bdc7000000b00611c616bc76so22966346ybk.5 for ; Mon, 17 Jan 2022 02:34:22 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=9DbAaJE5xrebfBmyzb7EUPwhc7BfIa7B186ejowDTcQ=; b=4eYLbtFvktJ1LUssX7/dR7EIirkuuLod9l+0VkODqp4VdA0PEJr9YNQYgfQGQgM5id /ivHLZS1TDVhgQt7XtI/Jt3TDluanGQLzY8NtUETL+hStRTDo19XBj1ffCe8gBPwMY7V 30aA1mHPE9cni5PbKeOR5neuqOVPWPhq0WW8ZvXyMImqpn7dBmCApbtavEHHQvcZ4p6W fk3KnVQVBD6dwj1rmzkowcY+lzMV32Oij0TTVPz84vyDmwjMUsBtACYSbqbDCPYB6cKS 5lgMZ2Z2iaoFXC4MuHclWTT1A51sMV8UJGRxW2jrdahP8sHCBbq/k88+ENMp6EnwAQlu gz9w== X-Gm-Message-State: AOAM532yodM5HOwnVqbNkSy/SRCCxkcqLj4pUg81BQrwW1wRiQKTvOC4 yLmugai0pzxudcu120VwCnrk6r5mZYO11iWUky4E85Wetf/9H1I0mde6RtyP4eyyFme8Y0yLw7y jJW5ap2PS6tWLbBmaM0/MO/ChV168Y08= X-Received: by 2002:a05:6902:702:: with SMTP id k2mr26813609ybt.66.1642415661896; Mon, 17 Jan 2022 02:34:21 -0800 (PST) X-Google-Smtp-Source: ABdhPJz8M/oCSeMYjYl9pH+l2JnXSaHMBYmVcpOV1h0/7glP2ko9g24As2QEfsJmlcM+zfUqG7nlkjox5GY7nmrG+64= X-Received: by 2002:a05:6902:702:: with SMTP id k2mr26813588ybt.66.1642415661586; Mon, 17 Jan 2022 02:34:21 -0800 (PST) MIME-Version: 1.0 References: <20220116180652.3694791-1-ppalka@redhat.com> In-Reply-To: <20220116180652.3694791-1-ppalka@redhat.com> From: Jonathan Wakely Date: Mon, 17 Jan 2022 10:34:09 +0000 Message-ID: Subject: Re: [PATCH 1/6 v2] libstdc++: Directly implement hexfloat std::from_chars for binary32/64 To: Patrick Palka Cc: gcc Patches , "libstdc++" X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, HTML_MESSAGE, KAM_LOTSOFHASH, KAM_NUMSUBJECT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org Content-Type: text/plain; charset="UTF-8" X-Content-Filtered-By: Mailman/MimeDel 2.1.29 X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 17 Jan 2022 10:34:31 -0000 On Sun, 16 Jan 2022 at 18:07, Patrick Palka via Libstdc++ < libstdc++@gcc.gnu.org> wrote: > Series tested on x86_64, i686, ppc64, ppc64le, aarch64, does it look OK for > trunk? > OK, thanks. > libstdc++-v3/ChangeLog: > > * src/c++17/floating_from_chars.cc: Include . > (ascii_to_hexit, starts_with_ci): Define. > (__floating_from_chars_hex): Define. > (from_chars): Conditionally call __floating_from_chars_hex. > (testsuite/20_util/from_chars/7.cc): New test. > --- > libstdc++-v3/src/c++17/floating_from_chars.cc | 376 ++++++++++++++++++ > .../testsuite/20_util/from_chars/7.cc | 151 +++++++ > 2 files changed, 527 insertions(+) > create mode 100644 libstdc++-v3/testsuite/20_util/from_chars/7.cc > > diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc > b/libstdc++-v3/src/c++17/floating_from_chars.cc > index 479e042bb5f..b186da9a955 100644 > --- a/libstdc++-v3/src/c++17/floating_from_chars.cc > +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc > @@ -31,6 +31,7 @@ > #define _GLIBCXX_USE_CXX11_ABI 1 > > #include > +#include > #include > #include > #include > @@ -396,6 +397,371 @@ namespace > } > #endif > > +#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 > + // If the given ASCII character represents a hexit, return that hexit. > + // Otherwise return -1. > + int > + ascii_to_hexit(char ch) > + { > + if (ch >= '0' && ch <= '9') > + return ch - '0'; > + if (ch >= 'a' && ch <= 'f') > + return ch - 'a' + 10; > + if (ch >= 'A' && ch <= 'F') > + return ch - 'A' + 10; > + return -1; > + } > + > + // Return true iff [FIRST,LAST) begins with PREFIX, ignoring case. > + bool > + starts_with_ci(const char* first, const char* last, string_view prefix) > + { > + __glibcxx_requires_valid_range(first, last); > + > + for (char ch : prefix) > + { > + __glibcxx_assert(ch >= 'a' && ch <= 'z'); > + if (first == last || (*first != ch && *first != ch - 32)) > + return false; > + ++first; > + } > + > + return true; > + } > + > + // An implementation of hexadecimal float parsing for binary32/64. > + template > + from_chars_result > + __floating_from_chars_hex(const char* first, const char* last, T& value) > + { > + static_assert(is_same_v || is_same_v); > + > + using uint_t = conditional_t, uint32_t, uint64_t>; > + constexpr int mantissa_width = is_same_v ? 23 : 52; > + constexpr int exponent_width = is_same_v ? 8 : 11; > + constexpr int exponent_bias = (1 << (exponent_width - 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 += 3; > + if (starts_with_ci(first, last, "inity"sv)) > + first += 5; > + > + uint_t result = 0; > + result |= sign_bit; > + result <<= exponent_width; > + result |= (1ull << exponent_width) - 1; > + result <<= mantissa_width; > + memcpy(&value, &result, sizeof(result)); > + > + return {first, errc{}}; > + } > + else if (starts_with_ci(first, last, "nan")) > + { > + first += 3; > + > + 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 >= '0' && ch <= '9') > + || (ch >= 'a' && ch <= 'z') > + || (ch >= 'A' && ch <= 'Z') > + || ch == '_') > + 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. > + uint_t result = 0; > + result <<= exponent_width; > + result |= (1ull << exponent_width) - 1; > + result <<= mantissa_width; > + result |= (1ull << (mantissa_width - 1)) | 1; > + memcpy(&value, &result, sizeof(result)); > + > + return {first, errc{}}; > + } > + } > + > + // Consume all insignificant leading zeros in the whole part of the > + // mantissa. > + bool seen_digit = false; > + while (first != last && *first == '0') > + { > + seen_digit = true; > + ++first; > + } > + > + // Now consume the rest of the written mantissa, populating MANTISSA > with the > + // first MANTISSA_WIDTH+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_width; // 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_WIDTH+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 = ascii_to_hexit(ch); > + if (hexit == -1) > + break; > + seen_digit = 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) > + { > + __glibcxx_assert(mantissa_idx == -1); > + mantissa |= hexit >> 1; > + midpoint_bit = (hexit & 0b0001) != 0; > + } > + else > + { > + __glibcxx_assert(mantissa_idx == -4); > + midpoint_bit = (hexit & 0b1000) != 0; > + nonzero_tail = (hexit & 0b0111) != 0; > + } > + } > + else > + nonzero_tail |= (hexit != 0x0); > + > + mantissa_idx -= 4; > + } > + if (mantissa != 0) > + __glibcxx_assert(__bit_width(mantissa) >= mantissa_width + 1 > + && __bit_width(mantissa) <= mantissa_width + 4); > + else > + __glibcxx_assert(!midpoint_bit && !nonzero_tail); > + > + if (!seen_digit) > + { > + // If we haven't seen any digit at this point, the parse failed. > + first = orig_first; > + return {first, errc::invalid_argument}; > + } > + > + // Parse the written exponent. > + int written_exponent = 0; > + if (first != last && *first == 'p') > + { > + // Tentatively consume the 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) > + // FIXME: Punt on large exponents for now. > + 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_width + 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.04p0" into "1.01p2". > + const int leading_hexit = mantissa >> mantissa_width; > + 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 a '1'. > + __glibcxx_assert((mantissa >> mantissa_width) == 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_width))); > + __glibcxx_assert(biased_exponent == 1); > + biased_exponent = 0; > + } > + > + // Perform round-to-nearest, tie-to-even rounding. > + 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_width)) > + { > + // 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_width + 1))) > + { > + // We rounded the normal number 1.fffffffffffff8pN (with > maximal > + // mantissa) up to to 1p(N+1). > + mantissa >>= 1; > + ++biased_exponent; > + } > + } > + else > + { > + // Rounding towards 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_width) - 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_width + mantissa_width); > + } > + else > + { > + // Assemble a nonzero normal or subnormal value. > + result |= sign_bit; > + result <<= exponent_width; > + result |= biased_exponent; > + result <<= mantissa_width; > + result |= mantissa & ((1ull << mantissa_width) - 1); > + // The implicit leading mantissa bit is set iff the number is > normal. > + __glibcxx_assert(((mantissa & (1ull << mantissa_width)) != 0) > + == (biased_exponent != 0)); > + } > + memcpy(&value, &result, sizeof(result)); > + > + return {first, errc{}}; > + } > +#endif > + > } // namespace > > // FIXME: This should be reimplemented so it doesn't use strtod and > newlocale. > @@ -406,6 +772,11 @@ from_chars_result > from_chars(const char* first, const char* last, float& value, > chars_format fmt) noexcept > { > +#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 > + if (fmt == chars_format::hex) > + return __floating_from_chars_hex(first, last, value); > +#endif > + > errc ec = errc::invalid_argument; > #if _GLIBCXX_USE_CXX11_ABI > buffer_resource mr; > @@ -432,6 +803,11 @@ from_chars_result > from_chars(const char* first, const char* last, double& value, > chars_format fmt) noexcept > { > +#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 > + if (fmt == chars_format::hex) > + return __floating_from_chars_hex(first, last, value); > +#endif > + > errc ec = errc::invalid_argument; > #if _GLIBCXX_USE_CXX11_ABI > buffer_resource mr; > diff --git a/libstdc++-v3/testsuite/20_util/from_chars/7.cc > b/libstdc++-v3/testsuite/20_util/from_chars/7.cc > new file mode 100644 > index 00000000000..090ad7a87bb > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/from_chars/7.cc > @@ -0,0 +1,151 @@ > +// Various testcases for binary64 hexfloat std::from_chars. > +// { dg-do run { target c++17 } } > +// { dg-require-effective-target ieee_floats } > + > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +struct testcase { > + const char* input; > + size_t correct_idx; > + std::errc correct_ec; > + double correct_value; > +}; > + > +constexpr testcase testcases[] = { > + { "1.fffffffffffff8p0", 18, {}, 0x1.fffffffffffff8p0 }, > + { "0.fffffffffffff8p-1022", 22, std::errc{}, 0x0.fffffffffffffep-1022 }, > + { "inf", 3, {}, std::numeric_limits::infinity() }, > + { "inff", 3, {}, std::numeric_limits::infinity() }, > + { "-inf", 4, {}, -std::numeric_limits::infinity() }, > + { "-inff", 4, {}, -std::numeric_limits::infinity() }, > + { "NAN", 3, {}, std::numeric_limits::quiet_NaN() }, > + { "-NAN", 4, {}, std::numeric_limits::quiet_NaN() }, > + { "NAN()", 5, {}, std::numeric_limits::quiet_NaN() }, > + { "-NAN()", 6, {}, std::numeric_limits::quiet_NaN() }, > + { "-NAN(test)", 10, {}, std::numeric_limits::quiet_NaN() }, > + { "-NAN(test", 4, {}, std::numeric_limits::quiet_NaN() }, > + { "-NAN(", 4, {}, std::numeric_limits::quiet_NaN() }, > + { "0.000000000000001p-100000000000000000", 37, > std::errc::result_out_of_range, 0 }, > + { "-lol", 0, std::errc::invalid_argument, 1 }, > + { " 0", 0, std::errc::invalid_argument, 1 }, > + { "", 0, std::errc::invalid_argument, 0 }, > + { "1", 1, {}, 1 }, > + { "2", 1, {}, 2 }, > + { "3", 1, {}, 3 }, > + { "4", 1, {}, 4 }, > + { "5", 1, {}, 5 }, > + { "6", 1, {}, 6 }, > + { "7", 1, {}, 7 }, > + { "8", 1, {}, 8 }, > + { "9", 1, {}, 9 }, > + { "a", 1, {}, 0xa }, > + { "b", 1, {}, 0xb }, > + { "c", 1, {}, 0xc }, > + { "d", 1, {}, 0xd }, > + { "e", 1, {}, 0xe }, > + { "f", 1, {}, 0xf }, > + { "0.000000000000000000000000000000000000000000001p-1022", 53, > + std::errc::result_out_of_range, 0 }, > + { "0.00000000000000p-1022", 22, {}, 0 }, > + { "0.00000000000009", 16, {}, 0x0.00000000000009p0 }, > + { "0.0", 3, {}, 0 }, > + { "1p10000000000000000000000", 25, std::errc::result_out_of_range, 0 }, > + { "-0.0", 4, {}, -0.0 }, > + { "0.00000000000000", 16, {}, 0 }, > + { "0.0000000000000p-1022", 21, {}, 0 }, > + { ".", 0, std::errc::invalid_argument, 0 }, > + { "-.", 0, std::errc::invalid_argument, 0 }, > + { "0", 1, {}, 0 }, > + { "00", 2, {}, 0 }, > + { "00.", 3, {}, 0 }, > + { "0.", 2, {}, 0 }, > + { "1.ffffFFFFFFFFFF", 16, {}, 0x2 }, > + { "1.ffffffffffffff", 16, {}, 0x2 }, > + { "1.00000000000029", 16, {}, 0x1.0000000000003p0 }, > + { "0.00000000000008p-1022", 22, std::errc::result_out_of_range, 0 }, > + { "1.fffffffffffffp-1023", 21, {}, 0x1p-1022 }, > + { "1.fffffffffffff8p+1023", 22, std::errc::result_out_of_range, 0 }, > + { "0.ffffffffffffe8p-1022", 22, {}, 0x0.ffffffffffffep-1022 }, > + { "2.11111111111111", 16, {}, 0x1.0888888888889p+1 }, > + { "1.1111111111111", 15, {}, 0x1.1111111111111p0 }, > + { "1.11111111111111", 16, {}, 0x1.1111111111111p0 }, > + { "1.11111111111118", 16, {}, 0x1.1111111111112p0 }, > + { "1.11111111111128", 16, {}, 0x1.1111111111112p0 }, > + { "1.1111111111112801", 18, {}, 0x1.1111111111113p0 }, > + { "1.08888888888888", 16, {}, 0x1.0888888888888p0 }, > + { "1.088888888888888", 17, {}, 0x1.0888888888889p0 }, > + { "2.00000000000029", 16, {}, 0x2.0000000000002p0 }, > + { "0.ffffffffffffep-1022", 21, {}, 0x0.ffffffffffffep-1022 }, > + { "3.ffffffffffffep-1024", 21, {}, 0x1p-1022 }, > + { "1.00000000000008p+0", 19, {}, 1 }, > + { "1p-1023", 7, {}, 0x0.8p-1022 }, > + { "1p-1022", 7, {}, 0x1p-1022 }, > + { "1.1p-1033", 9, {}, 0x1.1p-1033 }, // 0.0022p-1022 > + { "22p-1038", 8, {}, 0x1.1p-1033 }, > + { "5", 1, {}, 0x5 }, > + { "a", 1, {}, 0xa }, > + { "1", 1, {}, 1.0 }, > + { "1p1", 3, {}, 0x1p1 }, > + { "1p-1", 4, {}, 0x1p-1 }, > + { "0", 1, {}, 0.0 }, > + { "A", 1, {}, 0xA }, > + { "-1", 2, {}, -1.0 }, > + { "-0", 2, {}, -0.0 }, > + { "42", 2, {}, 0x42p0 }, > + { "-42", 3, {}, -0x42p0 }, > + { ".1", 2, {}, 0x0.1p0 }, > + { "22p-1000", 8, {}, 0x22p-1000 }, > + { ".0000008", 8, {}, 0x.0000008p0 }, > + { ".0000008p-1022", 14, {}, 0x.0000008p-1022 }, > + { "1p-1074", 7, {}, 0x.0000000000001p-1022 }, > + { "9999999999999", 13, {}, 0x9999999999999p0 }, > + { "1.000000000000a000", 18, {}, 0x1.000000000000ap0 }, > + { "1.000000000000a001", 18, {}, 0x1.000000000000ap0 }, > + { "1.000000000000a800", 18, {}, 0x1.000000000000ap0 }, > + { "1.000000000000a801", 18, {}, 0x1.000000000000bp0 }, > + { "1.000000000000b800", 18, {}, 0x1.000000000000cp0 }, > + { "000000", 6, {}, 0x0 }, > + { "1p", 1, {}, 0x1 }, > + { "0p99999999999999999999", 22, {}, 0 }, > + { "1p99999999999999999999", 22, std::errc::result_out_of_range, 0 }, > + { "0p-99999999999999999999", 23, {}, 0 }, > + { "1p-99999999999999999999", 23, std::errc::result_out_of_range, 0 }, > + { "99999999999999999999999", 23, {}, 0x99999999999999999999999p0 }, > + { "-1.fffffffffffffp-1023", 22, {}, -0x1p-1022 }, > + { "1.337", 5, {}, 0x1.337p0 }, > +}; > + > +void > +test01() > +{ > + for (auto [input,correct_idx,correct_ec,correct_value] : testcases) > + { > + double value; > + auto [ptr,ec] = std::from_chars(input, input+strlen(input), > + value, std::chars_format::hex); > + VERIFY( ptr == input + correct_idx ); > + VERIFY( ec == correct_ec ); > + if (ec == std::errc{}) > + { > + if (std::isnan(value) && std::isnan(value)) > + ; > + else > + { > + VERIFY( value == correct_value ); > + VERIFY( !memcmp(&value, &correct_value, sizeof(value)) ); > + } > + } > + } > +} > + > +int main() > +{ > + test01(); > +} > -- > 2.35.0.rc1 > >