Skip to content

Commit e3a86a8

Browse files
authored
Merge pull request #1180 from cppalliance/nan
2 parents 6a5da01 + fb51370 commit e3a86a8

File tree

7 files changed

+160
-23
lines changed

7 files changed

+160
-23
lines changed

doc/modules/ROOT/pages/cmath.adoc

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Additionally, all functions are marked `noexcept`.
1515
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].
1616

1717
== Basic Operations
18-
18+
[#basic_cmath_ops]
1919
|===
2020
| Function | Description
2121
| 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
2727
| https://en.cppreference.com/w/cpp/numeric/math/fmax[fmax] | Returns maximum value
2828
| https://en.cppreference.com/w/cpp/numeric/math/fmin[fmin] | Returns minimum value
2929
| https://en.cppreference.com/w/cpp/numeric/math/fdim[fdim] | Returns unsigned difference of two values
30-
| https://en.cppreference.com/w/cpp/numeric/math/nan[nan] | Generates NANs
30+
| https://en.cppreference.com/w/cpp/numeric/math/nan[nan] | Generates Quiet NANs with Payload
31+
| https://en.cppreference.com/w/cpp/numeric/math/nan[snan] | Generates Signaling NANs with Payload (Non-Standard)
3132
|===
3233

3334
[source, c++]
@@ -71,6 +72,10 @@ constexpr decimal32_t nand32(const char* arg) noexcept;
7172
constexpr decimal64_t nand64(const char* arg) noexcept;
7273
constexpr decimal128_t nand128(const char* arg) noexcept;
7374
75+
constexpr decimal32_t snand32(const char* arg) noexcept;
76+
constexpr decimal64_t snand64(const char* arg) noexcept;
77+
constexpr decimal128_t snand128(const char* arg) noexcept;
78+
7479
} // namespace decimal
7580
} // namespace boost
7681
----
@@ -521,7 +526,7 @@ constexpr DecimalType riemann_zeta(IntegralType n) noexcept;
521526

522527
The following are convenience functions, or are prescribed in IEEE 754-2019 as required for decimal floating point types.
523528

524-
=== issignaling
529+
=== `issignaling`
525530

526531
[source, c++]
527532
----
@@ -531,7 +536,20 @@ constexpr bool issignaling(Decimal x) noexcept;
531536

532537
Effects: If x is an sNaN returns true, otherwise returns false.
533538

534-
=== samequantum
539+
=== `read_payload`
540+
541+
[source, c++]
542+
----
543+
template <typename Decimal>
544+
constexpr auto read_payload(Decimal x) noexcept
545+
-> std::enable_if_t<is_ieee_type_v<Decimal>, typename T::significand_type>;
546+
----
547+
548+
Effects: if x is either a qNaN or an sNaN, returns the payload of the NaN, otherwise returns 0.
549+
550+
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.
551+
552+
=== `samequantum`
535553

536554
[source, c++]
537555
----
@@ -557,7 +575,7 @@ If both x and y are NaN, or infinity, they have the same quantum exponents.
557575

558576
If exactly one operand is infinity or exactly one operand is NaN, they do not have the same quantum exponents.
559577

560-
=== quantexp
578+
=== `quantexp`
561579

562580
[source, c++]
563581
----
@@ -581,7 +599,7 @@ Effects: if x is finite, returns its quantum exponent.
581599

582600
Otherwise, `INT_MIN` is returned.
583601

584-
=== quantized
602+
=== `quantized`
585603

586604
[source, c++]
587605
----
@@ -617,7 +635,7 @@ If both operands are infinity, the result is infinity, with the same sign as x.
617635

618636
The quantize functions do not signal underflow.
619637

620-
=== frexp10
638+
=== `frexp10`
621639

622640
[source, c++]
623641
----

include/boost/decimal/decimal128_t.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ BOOST_DECIMAL_EXPORT class decimal128_t final
215215
constexpr decimal128_t(const char* str, std::size_t len);
216216
#endif
217217

218+
template <typename T>
219+
friend constexpr auto read_payload(T value) noexcept
220+
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type);
221+
218222
public:
219223
// 3.2.4.1 construct/copy/destroy
220224
constexpr decimal128_t() noexcept = default;

include/boost/decimal/decimal32_t.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ BOOST_DECIMAL_EXPORT class decimal32_t final // NOLINT(cppcoreguidelines-special
225225
constexpr decimal32_t(const char* str, std::size_t len);
226226
#endif
227227

228+
template <typename T>
229+
friend constexpr auto read_payload(T value) noexcept
230+
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type);
231+
228232
public:
229233
// 3.2.2.1 construct/copy/destroy:
230234
constexpr decimal32_t() noexcept = default;

include/boost/decimal/decimal64_t.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ BOOST_DECIMAL_EXPORT class decimal64_t final
233233
constexpr decimal64_t(const char* str, std::size_t len);
234234
#endif
235235

236+
template <typename T>
237+
friend constexpr auto read_payload(T value) noexcept
238+
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type);
239+
236240
public:
237241
// 3.2.3.1 construct/copy/destroy
238242
constexpr decimal64_t() noexcept = default;

include/boost/decimal/detail/cmath/nan.hpp

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <boost/decimal/detail/config.hpp>
1010
#include <boost/decimal/detail/type_traits.hpp>
1111
#include <boost/decimal/detail/concepts.hpp>
12+
#include <boost/decimal/detail/utilities.hpp>
1213
#include <boost/decimal/cstdlib.hpp>
1314

1415
#ifndef BOOST_DECIMAL_BUILD_MODULE
@@ -22,36 +23,96 @@ namespace decimal {
2223

2324
namespace detail {
2425

25-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
26+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType, bool is_snan>
2627
constexpr auto nan_impl(const char* arg) noexcept -> TargetDecimalType
2728
{
28-
char* endptr {};
29-
const auto val {strtod_impl<TargetDecimalType>(arg, &endptr)};
30-
return val & std::numeric_limits<TargetDecimalType>::quiet_NaN();
29+
using sig_type = typename TargetDecimalType::significand_type;
30+
31+
constexpr TargetDecimalType nan_type {is_snan ? std::numeric_limits<TargetDecimalType>::signaling_NaN() :
32+
std::numeric_limits<TargetDecimalType>::quiet_NaN()};
33+
34+
constexpr std::uint32_t significand_field_bits {sizeof(TargetDecimalType) == sizeof(std::uint32_t) ? 23U :
35+
sizeof(TargetDecimalType) == sizeof(std::uint64_t) ? 53U : 110U};
36+
37+
constexpr sig_type max_payload_value {(static_cast<sig_type>(1) << (significand_field_bits + 1U)) - 1U};
38+
constexpr TargetDecimalType zero {};
39+
constexpr TargetDecimalType zero_bits {zero ^ zero};
40+
41+
sig_type payload_value {};
42+
const auto r {from_chars_integer_impl<sig_type, sig_type>(arg, arg + detail::strlen(arg), payload_value, 10)};
43+
44+
if (!r || payload_value > max_payload_value)
45+
{
46+
return nan_type;
47+
}
48+
else
49+
{
50+
return (zero_bits | payload_value) | nan_type;
51+
}
3152
}
3253

3354
} //namespace detail
3455

35-
BOOST_DECIMAL_EXPORT constexpr auto nand32(const char* arg) noexcept -> decimal32_t
56+
BOOST_DECIMAL_EXPORT template <typename T>
57+
constexpr auto nan(const char* arg) noexcept
58+
BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, T)
3659
{
37-
return detail::nan_impl<decimal32_t>(arg);
60+
return detail::nan_impl<T, false>(arg);
3861
}
3962

40-
BOOST_DECIMAL_EXPORT template <typename T>
41-
constexpr auto nan(const char* arg) noexcept
42-
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T)
63+
BOOST_DECIMAL_EXPORT constexpr auto nand32(const char* arg) noexcept -> decimal32_t
4364
{
44-
return detail::nan_impl<T>(arg);
65+
return detail::nan_impl<decimal32_t, false>(arg);
4566
}
4667

4768
BOOST_DECIMAL_EXPORT constexpr auto nand64(const char* arg) noexcept -> decimal64_t
4869
{
49-
return detail::nan_impl<decimal64_t>(arg);
70+
return detail::nan_impl<decimal64_t, false>(arg);
5071
}
5172

5273
BOOST_DECIMAL_EXPORT constexpr auto nand128(const char* arg) noexcept -> decimal128_t
5374
{
54-
return detail::nan_impl<decimal128_t>(arg);
75+
return detail::nan_impl<decimal128_t, false>(arg);
76+
}
77+
78+
BOOST_DECIMAL_EXPORT template <typename T>
79+
constexpr auto snan(const char* arg) noexcept
80+
BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, T)
81+
{
82+
return detail::nan_impl<T, true>(arg);
83+
}
84+
85+
BOOST_DECIMAL_EXPORT constexpr auto snand32(const char* arg) noexcept -> decimal32_t
86+
{
87+
return detail::nan_impl<decimal32_t, true>(arg);
88+
}
89+
90+
BOOST_DECIMAL_EXPORT constexpr auto snand64(const char* arg) noexcept -> decimal64_t
91+
{
92+
return detail::nan_impl<decimal64_t, true>(arg);
93+
}
94+
95+
BOOST_DECIMAL_EXPORT constexpr auto snand128(const char* arg) noexcept -> decimal128_t
96+
{
97+
return detail::nan_impl<decimal128_t, true>(arg);
98+
}
99+
100+
BOOST_DECIMAL_EXPORT template <typename T>
101+
constexpr auto read_payload(const T value) noexcept
102+
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type)
103+
{
104+
if (!isnan(value))
105+
{
106+
return 0U;
107+
}
108+
else if (issignaling(value))
109+
{
110+
return (value ^ std::numeric_limits<T>::signaling_NaN()).bits_;
111+
}
112+
else
113+
{
114+
return (value ^ std::numeric_limits<T>::quiet_NaN()).bits_;
115+
}
55116
}
56117

57118
} //namespace decimal

include/boost/decimal/fmt_format.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#ifdef __GNUC__
1313
# pragma GCC diagnostic push
1414
# pragma GCC diagnostic ignored "-Wfloat-equal"
15+
# pragma GCC diagnostic ignored "-Wconversion"
1516
#endif
1617

1718
#include <fmt/format.h>

test/test_cmath.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
#include <iostream>
4040
#include <random>
4141
#include <cmath>
42-
42+
#include <array>
4343

4444
#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) && !defined(_WIN32)
4545
static constexpr auto N = static_cast<std::size_t>(128U); // Number of trials
@@ -1237,13 +1237,58 @@ void test_exp2()
12371237
}
12381238

12391239
#if !defined(BOOST_DECIMAL_DISABLE_CLIB)
1240+
1241+
#if defined(__GNUC__) && __GNUC__ >= 8
1242+
# pragma GCC diagnostic push
1243+
# pragma GCC diagnostic ignored "-Wclass-memaccess"
1244+
#endif
1245+
12401246
template <typename T>
12411247
void test_nan()
12421248
{
1243-
BOOST_TEST(!isnan(nan<T>("1") & std::numeric_limits<T>::quiet_NaN()));
1244-
BOOST_TEST(!isnan(nan<T>("2") & std::numeric_limits<T>::quiet_NaN()));
1245-
BOOST_TEST(!isnan(nan<T>("-1") & std::numeric_limits<T>::quiet_NaN()));
1249+
using sig_type = typename T::significand_type;
1250+
1251+
const std::array<sig_type, 4> sigs {1U, 2U, 3U, 0U};
1252+
const std::array<const char*, 4> payloads {"1", "2", "3", "Junk"};
1253+
1254+
for (std::size_t i {}; i < sigs.size(); ++i)
1255+
{
1256+
const auto payload {nan<T>(payloads[i])};
1257+
BOOST_TEST(isnan(payload));
1258+
const auto removed_nan {payload ^ std::numeric_limits<T>::quiet_NaN()};
1259+
BOOST_TEST(!isnan(removed_nan));
1260+
1261+
// Check the payload
1262+
sig_type bits {};
1263+
std::memcpy(&bits, &removed_nan, sizeof(sig_type));
1264+
BOOST_TEST_EQ(bits, sigs[i]);
1265+
1266+
const auto payload_func_bits {read_payload(payload)};
1267+
BOOST_TEST_EQ(payload_func_bits, bits);
1268+
}
1269+
1270+
for (std::size_t i {}; i < sigs.size(); ++i)
1271+
{
1272+
const auto payload {snan<T>(payloads[i])};
1273+
BOOST_TEST(isnan(payload));
1274+
BOOST_TEST(issignaling(payload));
1275+
const auto removed_nan {payload ^ std::numeric_limits<T>::signaling_NaN()};
1276+
BOOST_TEST(!isnan(removed_nan));
1277+
1278+
// Check the payload
1279+
sig_type bits {};
1280+
std::memcpy(&bits, &removed_nan, sizeof(sig_type));
1281+
BOOST_TEST_EQ(bits, sigs[i]);
1282+
1283+
const auto payload_func_bits {read_payload(payload)};
1284+
BOOST_TEST_EQ(payload_func_bits, bits);
1285+
}
12461286
}
1287+
1288+
#if defined(__GNUC__) && __GNUC__ >= 8
1289+
# pragma GCC diagnostic pop
1290+
#endif
1291+
12471292
#endif
12481293

12491294
template <typename Dec>

0 commit comments

Comments
 (0)