From 03145fae9bc143f57aa76bd6e00d75cc0efb8eac Mon Sep 17 00:00:00 2001 From: Rinzii Date: Sat, 6 Apr 2024 02:00:38 -0400 Subject: [PATCH] Cleanup nan implementation and optimize for MSVC --- .../math/basic/impl/nan_double_impl.hpp | 157 ++++++++---------- 1 file changed, 68 insertions(+), 89 deletions(-) diff --git a/include/ccmath/math/basic/impl/nan_double_impl.hpp b/include/ccmath/math/basic/impl/nan_double_impl.hpp index 645431a..34e2646 100644 --- a/include/ccmath/math/basic/impl/nan_double_impl.hpp +++ b/include/ccmath/math/basic/impl/nan_double_impl.hpp @@ -10,112 +10,91 @@ #include "ccmath/internal/helpers/digit_to_int.hpp" #include "ccmath/internal/support/bits.hpp" - #include #include -namespace ccm::internal +namespace ccm::internal::impl { - namespace - { - namespace impl - { - - inline constexpr double nan_double_impl(const char * arg) noexcept - { - static_assert(std::numeric_limits::is_iec559, "IEEE-754 representation required for this implementation"); - - std::uint64_t dbl_bits{0}; - bool has_hex_been_detected{false}; - - if (arg == nullptr) - { - return std::numeric_limits::quiet_NaN(); // Default NaN - } - if (arg[0] == '\0') - { - return std::numeric_limits::quiet_NaN(); // Default NaN - } + constexpr double nan_double_impl(const char * arg) noexcept + { + static_assert(std::numeric_limits::is_iec559, "IEEE-754 representation required for this implementation"); - // NOLINTBEGIN +#if defined(_MSC_VER) && !defined(__clang__) + // Currently, MSVC always returns a Quiet NaN with no matter if a payload is + // provided or not. This is different from GCC and Clang which do allow payloads to be set. + // So if we detect we are using MSVC without Clang-CL then + // we can just return NaN and not bother doing any extra work. + // To properly mimic the behavior of MSVC. + return std::numeric_limits::quiet_NaN(); // Default NaN +#endif - // Check for a hex prefix and if its detected, skip the prefix and set the flag. - if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) - { - arg += 2; - has_hex_been_detected = true; - } + std::uint64_t dbl_bits{0}; + bool has_hex_been_detected{false}; - bool msvc_one_digit_patch{false}; + if (arg == nullptr) + { + return std::numeric_limits::quiet_NaN(); // Default NaN + } -#if defined(_MSC_VER) && !defined(__clang__) - // For some reason when passing '1' or '0x1' with msvc will cause it to adds on an extra bit to the number. - // I do not know why msvc does this, but it causes the number to be off by 1. - // This is a patch to fix that issue. + // NOLINTBEGIN + if (arg[0] == '\0') // + { + return std::numeric_limits::quiet_NaN(); // Default NaN + } - // Check that the last character is 1 and no other characters have been provided other than zero. - msvc_one_digit_patch = true; -#endif + // Check for a hex prefix and if its detected, skip the prefix and set the flag. + if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) + { + arg += 2; + has_hex_been_detected = true; + } - if (!has_hex_been_detected) + if (!has_hex_been_detected) + { + // Check that all of are characters are numbers. If we detect a non-number, return the default NaN. + for (std::size_t i = 0; arg[i] != '\0'; ++i) // NOLINT + { + if (arg[i] < '0' || arg[i] > '9') { - // Check that all of are characters are numbers. If we detect a non-number, return the default NaN. - for (std::size_t i = 0; arg[i] != '\0'; ++i) // NOLINT - { - if (arg[i] < '0' || arg[i] > '9') - { - return std::numeric_limits::quiet_NaN(); // Default NaN - } - } + return std::numeric_limits::quiet_NaN(); // Default NaN } + } + } - if (has_hex_been_detected) - { - // Calculate tag_value by handling wrapping for numbers larger than 8 digits - for (std::size_t i = 0; arg[i] != '\0'; ++i) - { - dbl_bits *= 16; - dbl_bits += static_cast(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value - if (i >= 15) - { - dbl_bits %= static_cast(1e18); // Wrap around for numbers larger than 8 digits - } - } - } - else + if (has_hex_been_detected) + { + // Calculate tag_value by handling wrapping for numbers larger than 8 digits + for (std::size_t i = 0; arg[i] != '\0'; ++i) + { + dbl_bits *= 16; + dbl_bits += static_cast(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value + if (i >= 15) { - // Calculate tag_value by handling wrapping for numbers larger than 8 digits - for (std::size_t i = 0; arg[i] != '\0'; ++i) - { - dbl_bits *= 10; - dbl_bits += static_cast(arg[i] - '0'); // Convert ASCII to numeric value - if (i >= 15) - { - dbl_bits %= static_cast(1e18); // Wrap around for numbers larger than 8 digits - } - } + dbl_bits %= static_cast(1e18); // Wrap around for numbers larger than 8 digits } - // NOLINTEND - - // Set the tag bits for NaN - // dbl_bits |= UINT64_C(0x7FF8000000000000); - dbl_bits |= ccm::support::bit_cast(std::numeric_limits::quiet_NaN()); - - // Subtract 1 bit from the number if the msvc patch is active - if (msvc_one_digit_patch) + } + } + else + { + // Calculate tag_value by handling wrapping for numbers larger than 8 digits + for (std::size_t i = 0; arg[i] != '\0'; ++i) + { + dbl_bits *= 10; + dbl_bits += static_cast(arg[i] - '0'); // Convert ASCII to numeric value + if (i >= 15) { - // TODO: Make this more efficient - // Currently, MSVC always returns a Quiet NaN with no additional bits set. - // This feature applies no matter what the input is. - return std::numeric_limits::quiet_NaN(); // Default NaN + dbl_bits %= static_cast(1e18); // Wrap around for numbers larger than 8 digits } + } + } + // NOLINTEND - // dbl_bits -= 1; + // Set the tag bits for NaN + // dbl_bits |= UINT64_C(0x7FF8000000000000); + dbl_bits |= ccm::support::bit_cast(std::numeric_limits::quiet_NaN()); - // Convert the uint64_t tag into a double NaN - return ccm::support::bit_cast(dbl_bits); - } - } // namespace impl - } // namespace -} // namespace ccm::internal + // Convert the uint64_t tag into a double NaN + return ccm::support::bit_cast(dbl_bits); + } +} // namespace ccm::internal::impl