Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions doc/modules/ROOT/pages/cmath.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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++]
Expand Down Expand Up @@ -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
----
Expand Down Expand Up @@ -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++]
----
Expand All @@ -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 <typename Decimal>
constexpr auto read_payload(Decimal x) noexcept
-> std::enable_if_t<is_ieee_type_v<Decimal>, 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++]
----
Expand All @@ -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++]
----
Expand All @@ -581,7 +599,7 @@ Effects: if x is finite, returns its quantum exponent.

Otherwise, `INT_MIN` is returned.

=== quantized
=== `quantized`

[source, c++]
----
Expand Down Expand Up @@ -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++]
----
Expand Down
4 changes: 4 additions & 0 deletions include/boost/decimal/decimal128_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ BOOST_DECIMAL_EXPORT class decimal128_t final
constexpr decimal128_t(const char* str, std::size_t len);
#endif

template <typename T>
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;
Expand Down
4 changes: 4 additions & 0 deletions include/boost/decimal/decimal32_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename T>
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;
Expand Down
4 changes: 4 additions & 0 deletions include/boost/decimal/decimal64_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ BOOST_DECIMAL_EXPORT class decimal64_t final
constexpr decimal64_t(const char* str, std::size_t len);
#endif

template <typename T>
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;
Expand Down
85 changes: 73 additions & 12 deletions include/boost/decimal/detail/cmath/nan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <boost/decimal/detail/config.hpp>
#include <boost/decimal/detail/type_traits.hpp>
#include <boost/decimal/detail/concepts.hpp>
#include <boost/decimal/detail/utilities.hpp>
#include <boost/decimal/cstdlib.hpp>

#ifndef BOOST_DECIMAL_BUILD_MODULE
Expand All @@ -22,36 +23,96 @@ namespace decimal {

namespace detail {

template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType, bool is_snan>
constexpr auto nan_impl(const char* arg) noexcept -> TargetDecimalType
{
char* endptr {};
const auto val {strtod_impl<TargetDecimalType>(arg, &endptr)};
return val & std::numeric_limits<TargetDecimalType>::quiet_NaN();
using sig_type = typename TargetDecimalType::significand_type;

constexpr TargetDecimalType nan_type {is_snan ? std::numeric_limits<TargetDecimalType>::signaling_NaN() :
std::numeric_limits<TargetDecimalType>::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<sig_type>(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<sig_type, sig_type>(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 <typename T>
constexpr auto nan(const char* arg) noexcept
BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, T)
{
return detail::nan_impl<decimal32_t>(arg);
return detail::nan_impl<T, false>(arg);
}

BOOST_DECIMAL_EXPORT template <typename T>
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<T>(arg);
return detail::nan_impl<decimal32_t, false>(arg);
}

BOOST_DECIMAL_EXPORT constexpr auto nand64(const char* arg) noexcept -> decimal64_t
{
return detail::nan_impl<decimal64_t>(arg);
return detail::nan_impl<decimal64_t, false>(arg);
}

BOOST_DECIMAL_EXPORT constexpr auto nand128(const char* arg) noexcept -> decimal128_t
{
return detail::nan_impl<decimal128_t>(arg);
return detail::nan_impl<decimal128_t, false>(arg);
}

BOOST_DECIMAL_EXPORT template <typename T>
constexpr auto snan(const char* arg) noexcept
BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, T)
{
return detail::nan_impl<T, true>(arg);
}

BOOST_DECIMAL_EXPORT constexpr auto snand32(const char* arg) noexcept -> decimal32_t
{
return detail::nan_impl<decimal32_t, true>(arg);
}

BOOST_DECIMAL_EXPORT constexpr auto snand64(const char* arg) noexcept -> decimal64_t
{
return detail::nan_impl<decimal64_t, true>(arg);
}

BOOST_DECIMAL_EXPORT constexpr auto snand128(const char* arg) noexcept -> decimal128_t
{
return detail::nan_impl<decimal128_t, true>(arg);
}

BOOST_DECIMAL_EXPORT template <typename T>
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<T>::signaling_NaN()).bits_;
}
else
{
return (value ^ std::numeric_limits<T>::quiet_NaN()).bits_;
}
}

} //namespace decimal
Expand Down
1 change: 1 addition & 0 deletions include/boost/decimal/fmt_format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
# pragma GCC diagnostic ignored "-Wconversion"
#endif

#include <fmt/format.h>
Expand Down
53 changes: 49 additions & 4 deletions test/test_cmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#include <iostream>
#include <random>
#include <cmath>

#include <array>

#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) && !defined(_WIN32)
static constexpr auto N = static_cast<std::size_t>(128U); // Number of trials
Expand Down Expand Up @@ -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 <typename T>
void test_nan()
{
BOOST_TEST(!isnan(nan<T>("1") & std::numeric_limits<T>::quiet_NaN()));
BOOST_TEST(!isnan(nan<T>("2") & std::numeric_limits<T>::quiet_NaN()));
BOOST_TEST(!isnan(nan<T>("-1") & std::numeric_limits<T>::quiet_NaN()));
using sig_type = typename T::significand_type;

const std::array<sig_type, 4> sigs {1U, 2U, 3U, 0U};
const std::array<const char*, 4> payloads {"1", "2", "3", "Junk"};

for (std::size_t i {}; i < sigs.size(); ++i)
{
const auto payload {nan<T>(payloads[i])};
BOOST_TEST(isnan(payload));
const auto removed_nan {payload ^ std::numeric_limits<T>::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<T>(payloads[i])};
BOOST_TEST(isnan(payload));
BOOST_TEST(issignaling(payload));
const auto removed_nan {payload ^ std::numeric_limits<T>::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 <typename Dec>
Expand Down