diff --git a/doc/modules/ROOT/pages/cmath.adoc b/doc/modules/ROOT/pages/cmath.adoc index 77ddd0e2b..d9946ce41 100644 --- a/doc/modules/ROOT/pages/cmath.adoc +++ b/doc/modules/ROOT/pages/cmath.adoc @@ -15,7 +15,7 @@ Additionally, all functions are marked `noexcept`. All of these functions are impacted by the global rounding mode as described in xref:cfenv.adoc[rounding modes] as well as the `DEC_FLT_EVAL_METHOD` from xref:cfloat.adoc[evaluation methods]. == Basic Operations - +[#basic_cmath_ops] |=== | Function | Description | https://en.cppreference.com/w/cpp/numeric/math/fabs[abs] | Absolute Value @@ -27,7 +27,8 @@ All of these functions are impacted by the global rounding mode as described in | https://en.cppreference.com/w/cpp/numeric/math/fmax[fmax] | Returns maximum value | https://en.cppreference.com/w/cpp/numeric/math/fmin[fmin] | Returns minimum value | https://en.cppreference.com/w/cpp/numeric/math/fdim[fdim] | Returns unsigned difference of two values -| https://en.cppreference.com/w/cpp/numeric/math/nan[nan] | Generates NANs +| https://en.cppreference.com/w/cpp/numeric/math/nan[nan] | Generates Quiet NANs with Payload +| https://en.cppreference.com/w/cpp/numeric/math/nan[snan] | Generates Signaling NANs with Payload (Non-Standard) |=== [source, c++] @@ -71,6 +72,10 @@ 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 decimal32_t snand32(const char* arg) noexcept; +constexpr decimal64_t snand64(const char* arg) noexcept; +constexpr decimal128_t snand128(const char* arg) noexcept; + } // namespace decimal } // namespace boost ---- @@ -521,7 +526,7 @@ constexpr DecimalType riemann_zeta(IntegralType n) noexcept; The following are convenience functions, or are prescribed in IEEE 754-2019 as required for decimal floating point types. -=== issignaling +=== `issignaling` [source, c++] ---- @@ -531,7 +536,20 @@ constexpr bool issignaling(Decimal x) noexcept; Effects: If x is an sNaN returns true, otherwise returns false. -=== samequantum +=== `read_payload` + +[source, c++] +---- +template +constexpr auto read_payload(Decimal x) noexcept + -> std::enable_if_t, typename T::significand_type>; +---- + +Effects: if x is either a qNaN or an sNaN, returns the payload of the NaN, otherwise returns 0. + +This function allows the payloads of nans that are set by the functions xref:basic_cmath_ops[`nan`] or xref:basic_cmath_ops[`snan`] to be returned. + +=== `samequantum` [source, c++] ---- @@ -557,7 +575,7 @@ If both x and y are NaN, or infinity, they have the same quantum exponents. If exactly one operand is infinity or exactly one operand is NaN, they do not have the same quantum exponents. -=== quantexp +=== `quantexp` [source, c++] ---- @@ -581,7 +599,7 @@ Effects: if x is finite, returns its quantum exponent. Otherwise, `INT_MIN` is returned. -=== quantized +=== `quantized` [source, c++] ---- @@ -617,7 +635,7 @@ If both operands are infinity, the result is infinity, with the same sign as x. The quantize functions do not signal underflow. -=== frexp10 +=== `frexp10` [source, c++] ---- diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index 66866ed91..81aa6e4b2 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -215,6 +215,10 @@ BOOST_DECIMAL_EXPORT class decimal128_t final constexpr decimal128_t(const char* str, std::size_t len); #endif + template + friend constexpr auto read_payload(T value) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type); + public: // 3.2.4.1 construct/copy/destroy constexpr decimal128_t() noexcept = default; diff --git a/include/boost/decimal/decimal32_t.hpp b/include/boost/decimal/decimal32_t.hpp index aa8cce3d3..9455b0e81 100644 --- a/include/boost/decimal/decimal32_t.hpp +++ b/include/boost/decimal/decimal32_t.hpp @@ -225,6 +225,10 @@ BOOST_DECIMAL_EXPORT class decimal32_t final // NOLINT(cppcoreguidelines-special constexpr decimal32_t(const char* str, std::size_t len); #endif + template + friend constexpr auto read_payload(T value) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type); + public: // 3.2.2.1 construct/copy/destroy: constexpr decimal32_t() noexcept = default; diff --git a/include/boost/decimal/decimal64_t.hpp b/include/boost/decimal/decimal64_t.hpp index 55acc7df3..d287234e5 100644 --- a/include/boost/decimal/decimal64_t.hpp +++ b/include/boost/decimal/decimal64_t.hpp @@ -233,6 +233,10 @@ BOOST_DECIMAL_EXPORT class decimal64_t final constexpr decimal64_t(const char* str, std::size_t len); #endif + template + friend constexpr auto read_payload(T value) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type); + public: // 3.2.3.1 construct/copy/destroy constexpr decimal64_t() noexcept = default; diff --git a/include/boost/decimal/detail/cmath/nan.hpp b/include/boost/decimal/detail/cmath/nan.hpp index 05629cc1d..8544ea45e 100644 --- a/include/boost/decimal/detail/cmath/nan.hpp +++ b/include/boost/decimal/detail/cmath/nan.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -22,36 +23,96 @@ namespace decimal { namespace detail { -template +template constexpr auto nan_impl(const char* arg) noexcept -> TargetDecimalType { - char* endptr {}; - const auto val {strtod_impl(arg, &endptr)}; - return val & std::numeric_limits::quiet_NaN(); + 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 {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 TargetDecimalType zero {}; + constexpr TargetDecimalType zero_bits {zero ^ zero}; + + sig_type payload_value {}; + const auto r {from_chars_integer_impl(arg, arg + detail::strlen(arg), payload_value, 10)}; + + if (!r || payload_value > max_payload_value) + { + return nan_type; + } + else + { + return (zero_bits | payload_value) | nan_type; + } } } //namespace detail -BOOST_DECIMAL_EXPORT constexpr auto nand32(const char* arg) noexcept -> decimal32_t +BOOST_DECIMAL_EXPORT template +constexpr auto nan(const char* arg) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, T) { - return detail::nan_impl(arg); + return detail::nan_impl(arg); } -BOOST_DECIMAL_EXPORT template -constexpr auto nan(const char* arg) noexcept - BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) +BOOST_DECIMAL_EXPORT constexpr auto nand32(const char* arg) noexcept -> decimal32_t { - return detail::nan_impl(arg); + return detail::nan_impl(arg); } BOOST_DECIMAL_EXPORT constexpr auto nand64(const char* arg) noexcept -> decimal64_t { - return detail::nan_impl(arg); + return detail::nan_impl(arg); } BOOST_DECIMAL_EXPORT constexpr auto nand128(const char* arg) noexcept -> decimal128_t { - return detail::nan_impl(arg); + 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) +{ + return detail::nan_impl(arg); +} + +BOOST_DECIMAL_EXPORT constexpr auto snand32(const char* arg) noexcept -> decimal32_t +{ + return detail::nan_impl(arg); +} + +BOOST_DECIMAL_EXPORT constexpr auto snand64(const char* arg) noexcept -> decimal64_t +{ + return detail::nan_impl(arg); +} + +BOOST_DECIMAL_EXPORT constexpr auto snand128(const char* arg) noexcept -> decimal128_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) +{ + if (!isnan(value)) + { + return 0U; + } + else if (issignaling(value)) + { + return (value ^ std::numeric_limits::signaling_NaN()).bits_; + } + else + { + return (value ^ std::numeric_limits::quiet_NaN()).bits_; + } } } //namespace decimal diff --git a/include/boost/decimal/fmt_format.hpp b/include/boost/decimal/fmt_format.hpp index b6bd16971..dfb3ea9e3 100644 --- a/include/boost/decimal/fmt_format.hpp +++ b/include/boost/decimal/fmt_format.hpp @@ -12,6 +12,7 @@ #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Wconversion" #endif #include diff --git a/test/test_cmath.cpp b/test/test_cmath.cpp index 787845724..c15fe3029 100644 --- a/test/test_cmath.cpp +++ b/test/test_cmath.cpp @@ -39,7 +39,7 @@ #include #include #include - +#include #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) && !defined(_WIN32) static constexpr auto N = static_cast(128U); // Number of trials @@ -1237,13 +1237,58 @@ void test_exp2() } #if !defined(BOOST_DECIMAL_DISABLE_CLIB) + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + template void test_nan() { - BOOST_TEST(!isnan(nan("1") & std::numeric_limits::quiet_NaN())); - BOOST_TEST(!isnan(nan("2") & std::numeric_limits::quiet_NaN())); - BOOST_TEST(!isnan(nan("-1") & std::numeric_limits::quiet_NaN())); + using sig_type = typename T::significand_type; + + const std::array sigs {1U, 2U, 3U, 0U}; + const std::array payloads {"1", "2", "3", "Junk"}; + + for (std::size_t i {}; i < sigs.size(); ++i) + { + const auto payload {nan(payloads[i])}; + BOOST_TEST(isnan(payload)); + const auto removed_nan {payload ^ std::numeric_limits::quiet_NaN()}; + BOOST_TEST(!isnan(removed_nan)); + + // Check the payload + sig_type bits {}; + std::memcpy(&bits, &removed_nan, sizeof(sig_type)); + BOOST_TEST_EQ(bits, sigs[i]); + + const auto payload_func_bits {read_payload(payload)}; + BOOST_TEST_EQ(payload_func_bits, bits); + } + + 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 removed_nan {payload ^ std::numeric_limits::signaling_NaN()}; + BOOST_TEST(!isnan(removed_nan)); + + // Check the payload + sig_type bits {}; + std::memcpy(&bits, &removed_nan, sizeof(sig_type)); + BOOST_TEST_EQ(bits, sigs[i]); + + const auto payload_func_bits {read_payload(payload)}; + BOOST_TEST_EQ(payload_func_bits, bits); + } } + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic pop +#endif + #endif template