diff --git a/doc/modules/ROOT/pages/cmath.adoc b/doc/modules/ROOT/pages/cmath.adoc index d9946ce41..4df937e19 100644 --- a/doc/modules/ROOT/pages/cmath.adoc +++ b/doc/modules/ROOT/pages/cmath.adoc @@ -72,10 +72,18 @@ constexpr decimal32_t nand32(const char* arg) noexcept; constexpr decimal64_t nand64(const char* arg) noexcept; constexpr decimal128_t nand128(const char* arg) noexcept; +constexpr decimal_fast32_t nand32f(const char* arg) noexcept; +constexpr decimal_fast64_t nand64f(const char* arg) noexcept; +constexpr decimal_fast128_t nand128f(const char* arg) noexcept; + constexpr decimal32_t snand32(const char* arg) noexcept; constexpr decimal64_t snand64(const char* arg) noexcept; constexpr decimal128_t snand128(const char* arg) noexcept; +constexpr decimal_fast32_t snand32f(const char* arg) noexcept; +constexpr decimal_fast64_t snand64f(const char* arg) noexcept; +constexpr decimal_fast128_t snand128f(const char* arg) noexcept; + } // namespace decimal } // namespace boost ---- @@ -542,7 +550,7 @@ Effects: If x is an sNaN returns true, otherwise returns false. ---- template constexpr auto read_payload(Decimal x) noexcept - -> std::enable_if_t, typename T::significand_type>; + -> typename T::significand_type; ---- Effects: if x is either a qNaN or an sNaN, returns the payload of the NaN, otherwise returns 0. diff --git a/include/boost/decimal/decimal_fast128_t.hpp b/include/boost/decimal/decimal_fast128_t.hpp index 4cd06a2f7..ecd94ea54 100644 --- a/include/boost/decimal/decimal_fast128_t.hpp +++ b/include/boost/decimal/decimal_fast128_t.hpp @@ -53,13 +53,13 @@ namespace decimal { namespace detail { -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_inf = boost::int128::uint128_t {UINT64_MAX - 2, UINT64_MAX}; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_qnan = boost::int128::uint128_t {UINT64_MAX - 1, UINT64_MAX}; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_snan = boost::int128::uint128_t {UINT64_MAX, UINT64_MAX}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_inf_high_bits {UINT64_C(0b1) << 61U}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_qnan_high_bits {UINT64_C(0b11) << 61U}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_snan_high_bits {UINT64_C(0b111) << 61U}; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_inf_high_bits = UINT64_MAX - 2; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_qnan_high_bits = UINT64_MAX - 1; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_snan_high_bits = UINT64_MAX; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_inf = boost::int128::uint128_t {d128_fast_inf_high_bits, 0U}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_qnan = boost::int128::uint128_t {d128_fast_qnan_high_bits, 0U}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d128_fast_snan = boost::int128::uint128_t {d128_fast_snan_high_bits, 0U}; template constexpr auto to_chars_scientific_impl(char* first, char* last, const TargetDecimalType& value, chars_format fmt) noexcept -> to_chars_result; @@ -73,6 +73,10 @@ constexpr auto to_chars_hex_impl(char* first, char* last, const TargetDecimalTyp template constexpr auto to_chars_cohort_preserving_scientific(char* first, char* last, const TargetDecimalType& value) noexcept -> to_chars_result; +template +constexpr auto nan_impl(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType); + } // namespace detail #ifdef _MSC_VER @@ -191,6 +195,14 @@ BOOST_DECIMAL_EXPORT class decimal_fast128_t final template friend constexpr auto detail::to_chars_cohort_preserving_scientific(char* first, char* last, const TargetDecimalType& value) noexcept -> to_chars_result; + template + friend constexpr auto detail::nan_impl(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType); + + template + friend constexpr auto read_payload(T value) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_fast_type_v, T, typename T::significand_type); + #if !defined(BOOST_DECIMAL_DISABLE_CLIB) constexpr decimal_fast128_t(const char* str, std::size_t len); #endif @@ -645,7 +657,7 @@ constexpr auto isnan(const decimal_fast128_t& val) noexcept -> bool constexpr auto issignaling(const decimal_fast128_t& val) noexcept -> bool { #ifndef BOOST_DECIMAL_FAST_MATH - return val.significand_.high == detail::d128_fast_snan_high_bits; + return val.significand_.high >= detail::d128_fast_snan_high_bits; #else static_cast(val); return false; diff --git a/include/boost/decimal/decimal_fast32_t.hpp b/include/boost/decimal/decimal_fast32_t.hpp index d6461379c..11de1002d 100644 --- a/include/boost/decimal/decimal_fast32_t.hpp +++ b/include/boost/decimal/decimal_fast32_t.hpp @@ -52,9 +52,9 @@ namespace decimal { namespace detail { -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d32_fast_inf = std::numeric_limits::max() - 3; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d32_fast_qnan = std::numeric_limits::max() - 2; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d32_fast_snan = std::numeric_limits::max() - 1; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d32_fast_inf {UINT32_C(0b1) << 29U}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d32_fast_qnan {UINT32_C(0b11) << 29U}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d32_fast_snan {UINT32_C(0b111) << 29U}; template constexpr auto to_chars_scientific_impl(char* first, char* last, const TargetDecimalType& value, chars_format fmt) noexcept -> to_chars_result; @@ -71,6 +71,10 @@ constexpr auto to_chars_cohort_preserving_scientific(char* first, char* last, co template constexpr auto d32_fma_impl(T x, T y, T z) noexcept -> T; +template +constexpr auto nan_impl(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType); + } // namespace detail BOOST_DECIMAL_EXPORT class decimal_fast32_t final @@ -191,6 +195,14 @@ BOOST_DECIMAL_EXPORT class decimal_fast32_t final template friend constexpr auto detail::generic_div_impl(const T& lhs, const T& rhs) noexcept -> DecimalType; + template + friend constexpr auto detail::nan_impl(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType); + + template + friend constexpr auto read_payload(T value) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_fast_type_v, T, typename T::significand_type); + #if !defined(BOOST_DECIMAL_DISABLE_CLIB) constexpr decimal_fast32_t(const char* str, std::size_t len); #endif @@ -631,7 +643,7 @@ constexpr auto isnan(const decimal_fast32_t val) noexcept -> bool constexpr auto issignaling(const decimal_fast32_t val) noexcept -> bool { #ifndef BOOST_DECIMAL_FAST_MATH - return val.significand_ == detail::d32_fast_snan; + return val.significand_ >= detail::d32_fast_snan; #else static_cast(val); return false; diff --git a/include/boost/decimal/decimal_fast64_t.hpp b/include/boost/decimal/decimal_fast64_t.hpp index 2e1edc669..91011a673 100644 --- a/include/boost/decimal/decimal_fast64_t.hpp +++ b/include/boost/decimal/decimal_fast64_t.hpp @@ -54,9 +54,9 @@ namespace decimal { namespace detail { -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d64_fast_inf = std::numeric_limits::max() - 3; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d64_fast_qnan = std::numeric_limits::max() - 2; -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d64_fast_snan = std::numeric_limits::max() - 1; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d64_fast_inf {UINT64_C(0b1) << 61U}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d64_fast_qnan {UINT64_C(0b11) << 61U}; +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE auto d64_fast_snan {UINT64_C(0b111) << 61U}; template constexpr auto to_chars_scientific_impl(char* first, char* last, const TargetDecimalType& value, chars_format fmt) noexcept -> to_chars_result; @@ -73,6 +73,10 @@ constexpr auto to_chars_cohort_preserving_scientific(char* first, char* last, co template constexpr auto d64_fma_impl(T x, T y, T z) noexcept -> T; +template +constexpr auto nan_impl(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType); + } // namespace detail BOOST_DECIMAL_EXPORT class decimal_fast64_t final @@ -198,6 +202,14 @@ BOOST_DECIMAL_EXPORT class decimal_fast64_t final template friend constexpr auto detail::d64_fma_impl(T x, T y, T z) noexcept -> T; + template + friend constexpr auto detail::nan_impl(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType); + + template + friend constexpr auto read_payload(T value) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_fast_type_v, T, typename T::significand_type); + #if !defined(BOOST_DECIMAL_DISABLE_CLIB) constexpr decimal_fast64_t(const char* str, std::size_t len); #endif @@ -638,7 +650,7 @@ constexpr auto isnan(const decimal_fast64_t val) noexcept -> bool constexpr auto issignaling(const decimal_fast64_t val) noexcept -> bool { #ifndef BOOST_DECIMAL_FAST_MATH - return val.significand_ == detail::d64_fast_snan; + return val.significand_ >= detail::d64_fast_snan; #else static_cast(val); return false; diff --git a/include/boost/decimal/detail/cmath/nan.hpp b/include/boost/decimal/detail/cmath/nan.hpp index 8544ea45e..7eed1fbe8 100644 --- a/include/boost/decimal/detail/cmath/nan.hpp +++ b/include/boost/decimal/detail/cmath/nan.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -23,8 +24,38 @@ namespace decimal { namespace detail { -template -constexpr auto nan_impl(const char* arg) noexcept -> TargetDecimalType +template +constexpr auto nan_impl(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType) +{ + using sig_type = typename TargetDecimalType::significand_type; + + constexpr TargetDecimalType nan_type {is_snan ? std::numeric_limits::signaling_NaN() : + std::numeric_limits::quiet_NaN()}; + + constexpr std::uint32_t significand_field_bits {decimal_val_v < 64 ? 23U : + decimal_val_v < 128 ? 53U : 110U}; + + constexpr sig_type max_payload_value {(static_cast(1) << significand_field_bits) - 1U}; + + sig_type payload_value {}; + const auto r {from_chars_integer_impl(arg, arg + detail::strlen(arg), payload_value, 10)}; + + TargetDecimalType return_value {nan_type}; + if (!r || payload_value > max_payload_value) + { + return return_value; + } + else + { + return_value.significand_ |= payload_value; + return return_value; + } +} + +template +constexpr auto nan_impl(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, TargetDecimalType) { using sig_type = typename TargetDecimalType::significand_type; @@ -34,7 +65,7 @@ constexpr auto nan_impl(const char* arg) noexcept -> TargetDecimalType constexpr std::uint32_t significand_field_bits {sizeof(TargetDecimalType) == sizeof(std::uint32_t) ? 23U : sizeof(TargetDecimalType) == sizeof(std::uint64_t) ? 53U : 110U}; - constexpr sig_type max_payload_value {(static_cast(1) << (significand_field_bits + 1U)) - 1U}; + constexpr sig_type max_payload_value {(static_cast(1) << significand_field_bits) - 1U}; constexpr TargetDecimalType zero {}; constexpr TargetDecimalType zero_bits {zero ^ zero}; @@ -55,7 +86,7 @@ constexpr auto nan_impl(const char* arg) noexcept -> TargetDecimalType BOOST_DECIMAL_EXPORT template constexpr auto nan(const char* arg) noexcept - BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, T) + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { return detail::nan_impl(arg); } @@ -75,9 +106,24 @@ BOOST_DECIMAL_EXPORT constexpr auto nand128(const char* arg) noexcept -> decimal return detail::nan_impl(arg); } +BOOST_DECIMAL_EXPORT constexpr auto nand32f(const char* arg) noexcept -> decimal_fast32_t +{ + return detail::nan_impl(arg); +} + +BOOST_DECIMAL_EXPORT constexpr auto nand64f(const char* arg) noexcept -> decimal_fast64_t +{ + return detail::nan_impl(arg); +} + +BOOST_DECIMAL_EXPORT constexpr auto nand128f(const char* arg) noexcept -> decimal_fast128_t +{ + return detail::nan_impl(arg); +} + BOOST_DECIMAL_EXPORT template constexpr auto snan(const char* arg) noexcept - BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, T) + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { return detail::nan_impl(arg); } @@ -97,6 +143,21 @@ BOOST_DECIMAL_EXPORT constexpr auto snand128(const char* arg) noexcept -> decima return detail::nan_impl(arg); } +BOOST_DECIMAL_EXPORT constexpr auto snand32f(const char* arg) noexcept -> decimal_fast32_t +{ + return detail::nan_impl(arg); +} + +BOOST_DECIMAL_EXPORT constexpr auto snand64f(const char* arg) noexcept -> decimal_fast64_t +{ + return detail::nan_impl(arg); +} + +BOOST_DECIMAL_EXPORT constexpr auto snand128f(const char* arg) noexcept -> decimal_fast128_t +{ + return detail::nan_impl(arg); +} + BOOST_DECIMAL_EXPORT template constexpr auto read_payload(const T value) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type) @@ -115,6 +176,70 @@ constexpr auto read_payload(const T value) noexcept } } +namespace detail { + +template +constexpr auto get_qnan_mask(); + +template <> +constexpr auto get_qnan_mask() +{ + return d32_fast_qnan; +} + +template <> +constexpr auto get_qnan_mask() +{ + return d64_fast_qnan; +} + +template <> +constexpr auto get_qnan_mask() +{ + return d128_fast_qnan; +} + +template +constexpr auto get_snan_mask(); + +template <> +constexpr auto get_snan_mask() +{ + return d32_fast_snan; +} + +template <> +constexpr auto get_snan_mask() +{ + return d64_fast_snan; +} + +template <> +constexpr auto get_snan_mask() +{ + return d128_fast_snan; +} + +} // namespace detail + +BOOST_DECIMAL_EXPORT template +constexpr auto read_payload(const T value) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_fast_type_v, T, typename T::significand_type) +{ + if (!isnan(value)) + { + return 0U; + } + else if (issignaling(value)) + { + return value.significand_ ^ detail::get_snan_mask(); + } + else + { + return value.significand_ ^ detail::get_qnan_mask(); + } +} + } //namespace decimal } //namespace boost diff --git a/test/test_cmath.cpp b/test/test_cmath.cpp index c15fe3029..dc9a86bda 100644 --- a/test/test_cmath.cpp +++ b/test/test_cmath.cpp @@ -1244,17 +1244,19 @@ void test_exp2() #endif template -void test_nan() +auto test_nan() + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, void) { using sig_type = typename T::significand_type; - const std::array sigs {1U, 2U, 3U, 0U}; - const std::array payloads {"1", "2", "3", "Junk"}; + const std::array sigs {1U, 2U, 3U, 0U, 0U}; + constexpr std::array payloads {"1", "2", "3", "Junk", "999999999999999999999999999999999999999999999999999999999999"}; for (std::size_t i {}; i < sigs.size(); ++i) { const auto payload {nan(payloads[i])}; BOOST_TEST(isnan(payload)); + BOOST_TEST(!issignaling(payload)); const auto removed_nan {payload ^ std::numeric_limits::quiet_NaN()}; BOOST_TEST(!isnan(removed_nan)); @@ -1285,6 +1287,36 @@ void test_nan() } } +template +auto test_nan() + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_fast_type_v, T, void) +{ + using sig_type = typename T::significand_type; + + const std::array sigs {1U, 2U, 3U, 0U, 0U}; + constexpr std::array payloads {"1", "2", "3", "Junk", "999999999999999999999999999999999999999999999999999999999999"}; + + for (std::size_t i {}; i < sigs.size(); ++i) + { + const auto payload {nan(payloads[i])}; + BOOST_TEST(isnan(payload)); + BOOST_TEST(!issignaling(payload)); + + const auto recovered_payload {read_payload(payload)}; + BOOST_TEST_EQ(recovered_payload, sigs[i]); + } + + for (std::size_t i {}; i < sigs.size(); ++i) + { + const auto payload {snan(payloads[i])}; + BOOST_TEST(isnan(payload)); + BOOST_TEST(issignaling(payload)); + + const auto recovered_payload {read_payload(payload)}; + BOOST_TEST_EQ(recovered_payload, sigs[i]); + } +} + #if defined(__GNUC__) && __GNUC__ >= 8 # pragma GCC diagnostic pop #endif @@ -1591,6 +1623,10 @@ int main() test_nan(); test_nan(); test_nan(); + + test_nan(); + test_nan(); + test_nan(); #endif test_log2();