Skip to content

Commit 9051a03

Browse files
authored
Merge pull request #1253 from cppalliance/dectest_add
Fix handling of SNAN in add, sub, mul, div, mod
2 parents eef734d + 5ce4cbb commit 9051a03

File tree

11 files changed

+1178
-765
lines changed

11 files changed

+1178
-765
lines changed

include/boost/decimal/decimal128_t.hpp

Lines changed: 145 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,21 @@ BOOST_DECIMAL_EXPORT class decimal128_t final
220220
friend constexpr auto read_payload(T value) noexcept
221221
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type);
222222

223+
friend constexpr auto nan_conversion(const decimal128_t value) noexcept -> decimal128_t
224+
{
225+
constexpr auto convert_nan_mask {detail::d128_snan_mask ^ detail::d128_nan_mask};
226+
227+
decimal128_t return_value;
228+
return_value.bits_ = value.bits_ ^ convert_nan_mask;
229+
return return_value;
230+
}
231+
232+
template <typename Decimal>
233+
friend constexpr Decimal detail::check_non_finite(Decimal lhs, Decimal rhs) noexcept;
234+
235+
template <typename Decimal>
236+
friend constexpr Decimal detail::check_non_finite(Decimal x) noexcept;
237+
223238
public:
224239
// 3.2.4.1 construct/copy/destroy
225240
constexpr decimal128_t() noexcept = default;
@@ -885,6 +900,112 @@ constexpr decimal128_t::decimal128_t(const bool value) noexcept : decimal128_t(s
885900
# pragma GCC diagnostic pop
886901
#endif
887902

903+
namespace detail {
904+
905+
template <bool>
906+
class numeric_limits_impl128
907+
{
908+
public:
909+
910+
static constexpr bool is_specialized = true;
911+
static constexpr bool is_signed = true;
912+
static constexpr bool is_integer = false;
913+
static constexpr bool is_exact = false;
914+
static constexpr bool has_infinity = true;
915+
static constexpr bool has_quiet_NaN = true;
916+
static constexpr bool has_signaling_NaN = true;
917+
918+
// These members were deprecated in C++23
919+
#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L)))
920+
static constexpr std::float_denorm_style has_denorm = std::denorm_present;
921+
static constexpr bool has_denorm_loss = true;
922+
#endif
923+
924+
static constexpr std::float_round_style round_style = std::round_indeterminate;
925+
static constexpr bool is_iec559 = true;
926+
static constexpr bool is_bounded = true;
927+
static constexpr bool is_modulo = false;
928+
static constexpr int digits = 34;
929+
static constexpr int digits10 = digits;
930+
static constexpr int max_digits10 = digits;
931+
static constexpr int radix = 10;
932+
static constexpr int min_exponent = -6143;
933+
static constexpr int min_exponent10 = min_exponent;
934+
static constexpr int max_exponent = 6144;
935+
static constexpr int max_exponent10 = max_exponent;
936+
static constexpr bool traps = std::numeric_limits<std::uint64_t>::traps;
937+
static constexpr bool tinyness_before = true;
938+
939+
// Member functions
940+
static constexpr auto (min) () -> boost::decimal::decimal128_t { return {UINT32_C(1), min_exponent}; }
941+
static constexpr auto (max) () -> boost::decimal::decimal128_t { return {boost::int128::uint128_t{UINT64_C(0b1111011010000100110111110101011011000011111000000), UINT64_C(0b0011011110001101100011100110001111111111111111111111111111111111)}, max_exponent - digits + 1}; }
942+
static constexpr auto lowest () -> boost::decimal::decimal128_t { return {boost::int128::uint128_t{UINT64_C(0b1111011010000100110111110101011011000011111000000), UINT64_C(0b0011011110001101100011100110001111111111111111111111111111111111)}, max_exponent - digits + 1, construction_sign::negative}; }
943+
static constexpr auto epsilon () -> boost::decimal::decimal128_t { return {UINT32_C(1), -digits + 1}; }
944+
static constexpr auto round_error () -> boost::decimal::decimal128_t { return epsilon(); }
945+
static constexpr auto infinity () -> boost::decimal::decimal128_t { return boost::decimal::from_bits(boost::decimal::detail::d128_inf_mask); }
946+
static constexpr auto quiet_NaN () -> boost::decimal::decimal128_t { return boost::decimal::from_bits(boost::decimal::detail::d128_nan_mask); }
947+
static constexpr auto signaling_NaN() -> boost::decimal::decimal128_t { return boost::decimal::from_bits(boost::decimal::detail::d128_snan_mask); }
948+
static constexpr auto denorm_min () -> boost::decimal::decimal128_t { return {1, boost::decimal::detail::etiny_v<boost::decimal::decimal128_t>}; }
949+
};
950+
951+
#if !defined(__cpp_inline_variables) || __cpp_inline_variables < 201606L
952+
953+
template <bool b> constexpr bool numeric_limits_impl128<b>::is_specialized;
954+
template <bool b> constexpr bool numeric_limits_impl128<b>::is_signed;
955+
template <bool b> constexpr bool numeric_limits_impl128<b>::is_integer;
956+
template <bool b> constexpr bool numeric_limits_impl128<b>::is_exact;
957+
template <bool b> constexpr bool numeric_limits_impl128<b>::has_infinity;
958+
template <bool b> constexpr bool numeric_limits_impl128<b>::has_quiet_NaN;
959+
template <bool b> constexpr bool numeric_limits_impl128<b>::has_signaling_NaN;
960+
961+
// These members were deprecated in C++23
962+
#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L)))
963+
template <bool b> constexpr std::float_denorm_style numeric_limits_impl128<b>::has_denorm;
964+
template <bool b> constexpr bool numeric_limits_impl128<b>::has_denorm_loss;
965+
#endif
966+
967+
template <bool b> constexpr std::float_round_style numeric_limits_impl128<b>::round_style;
968+
template <bool b> constexpr bool numeric_limits_impl128<b>::is_iec559;
969+
template <bool b> constexpr bool numeric_limits_impl128<b>::is_bounded;
970+
template <bool b> constexpr bool numeric_limits_impl128<b>::is_modulo;
971+
template <bool b> constexpr int numeric_limits_impl128<b>::digits;
972+
template <bool b> constexpr int numeric_limits_impl128<b>::digits10;
973+
template <bool b> constexpr int numeric_limits_impl128<b>::max_digits10;
974+
template <bool b> constexpr int numeric_limits_impl128<b>::radix;
975+
template <bool b> constexpr int numeric_limits_impl128<b>::min_exponent;
976+
template <bool b> constexpr int numeric_limits_impl128<b>::min_exponent10;
977+
template <bool b> constexpr int numeric_limits_impl128<b>::max_exponent;
978+
template <bool b> constexpr int numeric_limits_impl128<b>::max_exponent10;
979+
template <bool b> constexpr bool numeric_limits_impl128<b>::traps;
980+
template <bool b> constexpr bool numeric_limits_impl128<b>::tinyness_before;
981+
982+
#endif // !defined(__cpp_inline_variables) || __cpp_inline_variables < 201606L
983+
984+
} // namespace detail
985+
986+
} //namespace decimal
987+
} //namespace boost
988+
989+
namespace std {
990+
991+
#ifdef __clang__
992+
# pragma clang diagnostic push
993+
# pragma clang diagnostic ignored "-Wmismatched-tags"
994+
#endif
995+
996+
template <>
997+
class numeric_limits<boost::decimal::decimal128_t> :
998+
public boost::decimal::detail::numeric_limits_impl128<true> {};
999+
1000+
#ifdef __clang__
1001+
# pragma clang diagnostic pop
1002+
#endif
1003+
1004+
} //namespace std
1005+
1006+
namespace boost {
1007+
namespace decimal {
1008+
8881009
#if defined(__clang__)
8891010
# pragma clang diagnostic push
8901011
# pragma clang diagnostic ignored "-Wfloat-equal"
@@ -1462,8 +1583,20 @@ constexpr auto d128_div_impl(const decimal128_t& lhs, const decimal128_t& rhs, d
14621583

14631584
if (lhs_fp == FP_NAN || rhs_fp == FP_NAN)
14641585
{
1465-
q = nan;
1466-
r = nan;
1586+
// Operations on an SNAN return a QNAN with the same payload
1587+
decimal128_t return_nan {};
1588+
if (lhs_fp == FP_NAN)
1589+
{
1590+
return_nan = issignaling(lhs) ? nan_conversion(lhs) : lhs;
1591+
}
1592+
else
1593+
{
1594+
return_nan = issignaling(rhs) ? nan_conversion(rhs) : rhs;
1595+
}
1596+
1597+
q = return_nan;
1598+
r = return_nan;
1599+
14671600
return;
14681601
}
14691602

@@ -1579,7 +1712,7 @@ constexpr auto operator+(const decimal128_t lhs, const Integer rhs) noexcept
15791712
#ifndef BOOST_DECIMAL_FAST_MATH
15801713
if (not_finite(lhs))
15811714
{
1582-
return lhs;
1715+
return detail::check_non_finite(lhs);
15831716
}
15841717
#endif
15851718

@@ -1643,7 +1776,7 @@ constexpr auto operator-(const decimal128_t lhs, const Integer rhs) noexcept
16431776
#ifndef BOOST_DECIMAL_FAST_MATH
16441777
if (not_finite(lhs))
16451778
{
1646-
return lhs;
1779+
return detail::check_non_finite(lhs);
16471780
}
16481781
#endif
16491782

@@ -1672,7 +1805,7 @@ constexpr auto operator-(const Integer lhs, const decimal128_t rhs) noexcept
16721805
#ifndef BOOST_DECIMAL_FAST_MATH
16731806
if (not_finite(rhs))
16741807
{
1675-
return rhs;
1808+
return detail::check_non_finite(rhs);
16761809
}
16771810
#endif
16781811

@@ -1723,7 +1856,7 @@ constexpr auto operator*(const decimal128_t lhs, const Integer rhs) noexcept
17231856
#ifndef BOOST_DECIMAL_FAST_MATH
17241857
if (not_finite(lhs))
17251858
{
1726-
return lhs;
1859+
return detail::check_non_finite(lhs);
17271860
}
17281861
#endif
17291862

@@ -1767,8 +1900,7 @@ constexpr auto operator/(const decimal128_t lhs, const Integer rhs) noexcept
17671900
#ifndef BOOST_DECIMAL_FAST_MATH
17681901
// Check pre-conditions
17691902
constexpr decimal128_t zero {0, 0};
1770-
constexpr decimal128_t nan {boost::decimal::from_bits(boost::decimal::detail::d128_snan_mask)};
1771-
constexpr decimal128_t inf {boost::decimal::from_bits(boost::decimal::detail::d128_inf_mask)};
1903+
constexpr decimal128_t inf {from_bits(detail::d128_inf_mask)};
17721904

17731905
const bool sign {lhs.isneg() != (rhs < 0)};
17741906

@@ -1777,9 +1909,9 @@ constexpr auto operator/(const decimal128_t lhs, const Integer rhs) noexcept
17771909
switch (lhs_fp)
17781910
{
17791911
case FP_NAN:
1780-
return nan;
1912+
return issignaling(lhs) ? nan_conversion(lhs) : lhs;
17811913
case FP_INFINITE:
1782-
return inf;
1914+
return lhs;
17831915
case FP_ZERO:
17841916
return sign ? -zero : zero;
17851917
default:
@@ -1810,20 +1942,16 @@ constexpr auto operator/(const Integer lhs, const decimal128_t rhs) noexcept
18101942
#ifndef BOOST_DECIMAL_FAST_MATH
18111943
// Check pre-conditions
18121944
constexpr decimal128_t zero {0, 0};
1813-
constexpr decimal128_t inf {boost::decimal::from_bits(boost::decimal::detail::d128_inf_mask)};
1814-
constexpr decimal128_t nan {boost::decimal::from_bits(boost::decimal::detail::d128_snan_mask)};
1945+
constexpr decimal128_t inf {from_bits(detail::d128_inf_mask)};
18151946

18161947
const bool sign {(lhs < 0) != rhs.isneg()};
18171948

18181949
const auto rhs_fp {fpclassify(rhs)};
18191950

1820-
if (rhs_fp == FP_NAN)
1821-
{
1822-
return nan;
1823-
}
1824-
18251951
switch (rhs_fp)
18261952
{
1953+
case FP_NAN:
1954+
return issignaling(rhs) ? nan_conversion(rhs) : rhs;
18271955
case FP_INFINITE:
18281956
return sign ? -zero : zero;
18291957
case FP_ZERO:
@@ -2176,112 +2304,6 @@ constexpr auto scalbnd128(decimal128_t num, const int expval) noexcept -> decima
21762304
return scalblnd128(num, static_cast<long>(expval));
21772305
}
21782306

2179-
namespace detail {
2180-
2181-
template <bool>
2182-
class numeric_limits_impl128
2183-
{
2184-
public:
2185-
2186-
static constexpr bool is_specialized = true;
2187-
static constexpr bool is_signed = true;
2188-
static constexpr bool is_integer = false;
2189-
static constexpr bool is_exact = false;
2190-
static constexpr bool has_infinity = true;
2191-
static constexpr bool has_quiet_NaN = true;
2192-
static constexpr bool has_signaling_NaN = true;
2193-
2194-
// These members were deprecated in C++23
2195-
#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L)))
2196-
static constexpr std::float_denorm_style has_denorm = std::denorm_present;
2197-
static constexpr bool has_denorm_loss = true;
2198-
#endif
2199-
2200-
static constexpr std::float_round_style round_style = std::round_indeterminate;
2201-
static constexpr bool is_iec559 = true;
2202-
static constexpr bool is_bounded = true;
2203-
static constexpr bool is_modulo = false;
2204-
static constexpr int digits = 34;
2205-
static constexpr int digits10 = digits;
2206-
static constexpr int max_digits10 = digits;
2207-
static constexpr int radix = 10;
2208-
static constexpr int min_exponent = -6143;
2209-
static constexpr int min_exponent10 = min_exponent;
2210-
static constexpr int max_exponent = 6144;
2211-
static constexpr int max_exponent10 = max_exponent;
2212-
static constexpr bool traps = std::numeric_limits<std::uint64_t>::traps;
2213-
static constexpr bool tinyness_before = true;
2214-
2215-
// Member functions
2216-
static constexpr auto (min) () -> boost::decimal::decimal128_t { return {UINT32_C(1), min_exponent}; }
2217-
static constexpr auto (max) () -> boost::decimal::decimal128_t { return {boost::int128::uint128_t{UINT64_C(0b1111011010000100110111110101011011000011111000000), UINT64_C(0b0011011110001101100011100110001111111111111111111111111111111111)}, max_exponent - digits + 1}; }
2218-
static constexpr auto lowest () -> boost::decimal::decimal128_t { return {boost::int128::uint128_t{UINT64_C(0b1111011010000100110111110101011011000011111000000), UINT64_C(0b0011011110001101100011100110001111111111111111111111111111111111)}, max_exponent - digits + 1, construction_sign::negative}; }
2219-
static constexpr auto epsilon () -> boost::decimal::decimal128_t { return {UINT32_C(1), -digits + 1}; }
2220-
static constexpr auto round_error () -> boost::decimal::decimal128_t { return epsilon(); }
2221-
static constexpr auto infinity () -> boost::decimal::decimal128_t { return boost::decimal::from_bits(boost::decimal::detail::d128_inf_mask); }
2222-
static constexpr auto quiet_NaN () -> boost::decimal::decimal128_t { return boost::decimal::from_bits(boost::decimal::detail::d128_nan_mask); }
2223-
static constexpr auto signaling_NaN() -> boost::decimal::decimal128_t { return boost::decimal::from_bits(boost::decimal::detail::d128_snan_mask); }
2224-
static constexpr auto denorm_min () -> boost::decimal::decimal128_t { return {1, boost::decimal::detail::etiny_v<boost::decimal::decimal128_t>}; }
2225-
};
2226-
2227-
#if !defined(__cpp_inline_variables) || __cpp_inline_variables < 201606L
2228-
2229-
template <bool b> constexpr bool numeric_limits_impl128<b>::is_specialized;
2230-
template <bool b> constexpr bool numeric_limits_impl128<b>::is_signed;
2231-
template <bool b> constexpr bool numeric_limits_impl128<b>::is_integer;
2232-
template <bool b> constexpr bool numeric_limits_impl128<b>::is_exact;
2233-
template <bool b> constexpr bool numeric_limits_impl128<b>::has_infinity;
2234-
template <bool b> constexpr bool numeric_limits_impl128<b>::has_quiet_NaN;
2235-
template <bool b> constexpr bool numeric_limits_impl128<b>::has_signaling_NaN;
2236-
2237-
// These members were deprecated in C++23
2238-
#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L)))
2239-
template <bool b> constexpr std::float_denorm_style numeric_limits_impl128<b>::has_denorm;
2240-
template <bool b> constexpr bool numeric_limits_impl128<b>::has_denorm_loss;
2241-
#endif
2242-
2243-
template <bool b> constexpr std::float_round_style numeric_limits_impl128<b>::round_style;
2244-
template <bool b> constexpr bool numeric_limits_impl128<b>::is_iec559;
2245-
template <bool b> constexpr bool numeric_limits_impl128<b>::is_bounded;
2246-
template <bool b> constexpr bool numeric_limits_impl128<b>::is_modulo;
2247-
template <bool b> constexpr int numeric_limits_impl128<b>::digits;
2248-
template <bool b> constexpr int numeric_limits_impl128<b>::digits10;
2249-
template <bool b> constexpr int numeric_limits_impl128<b>::max_digits10;
2250-
template <bool b> constexpr int numeric_limits_impl128<b>::radix;
2251-
template <bool b> constexpr int numeric_limits_impl128<b>::min_exponent;
2252-
template <bool b> constexpr int numeric_limits_impl128<b>::min_exponent10;
2253-
template <bool b> constexpr int numeric_limits_impl128<b>::max_exponent;
2254-
template <bool b> constexpr int numeric_limits_impl128<b>::max_exponent10;
2255-
template <bool b> constexpr bool numeric_limits_impl128<b>::traps;
2256-
template <bool b> constexpr bool numeric_limits_impl128<b>::tinyness_before;
2257-
2258-
#endif // !defined(__cpp_inline_variables) || __cpp_inline_variables < 201606L
2259-
2260-
} // namespace detail
2261-
2262-
} //namespace decimal
2263-
} //namespace boost
2264-
2265-
namespace std {
2266-
2267-
#ifdef __clang__
2268-
# pragma clang diagnostic push
2269-
# pragma clang diagnostic ignored "-Wmismatched-tags"
2270-
#endif
2271-
2272-
template <>
2273-
class numeric_limits<boost::decimal::decimal128_t> :
2274-
public boost::decimal::detail::numeric_limits_impl128<true> {};
2275-
2276-
#ifdef __clang__
2277-
# pragma clang diagnostic pop
2278-
#endif
2279-
2280-
} //namespace std
2281-
2282-
namespace boost {
2283-
namespace decimal {
2284-
22852307
#if !defined(BOOST_DECIMAL_DISABLE_CLIB)
22862308

22872309
constexpr decimal128_t::decimal128_t(const char* str, std::size_t len)

0 commit comments

Comments
 (0)