diff --git a/include/boost/decimal/charconv.hpp b/include/boost/decimal/charconv.hpp index ecc279d2..6bef449d 100644 --- a/include/boost/decimal/charconv.hpp +++ b/include/boost/decimal/charconv.hpp @@ -780,11 +780,11 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_impl(char* first, char* last, TargetDecima } auto abs_value = abs(value); - constexpr auto max_fractional_value = impl::decimal_val_v < 64 ? TargetDecimalType{1, 7} : - impl::decimal_val_v < 128 ? TargetDecimalType{1, 16} : - TargetDecimalType{1, 34}; + const auto max_fractional_value = impl::decimal_val_v < 64 ? TargetDecimalType{1, 7} : + impl::decimal_val_v < 128 ? TargetDecimalType{1, 16} : + TargetDecimalType{1, 34}; - constexpr auto min_fractional_value = TargetDecimalType{1, -4}; + const auto min_fractional_value = TargetDecimalType{1, -4}; // Unspecified precision so we always go with the shortest representation if (precision == -1) diff --git a/include/boost/decimal/decimal32.hpp b/include/boost/decimal/decimal32.hpp index 22d35513..1e34a03a 100644 --- a/include/boost/decimal/decimal32.hpp +++ b/include/boost/decimal/decimal32.hpp @@ -143,6 +143,9 @@ BOOST_DECIMAL_EXPORT class decimal32 final // NOLINT(cppcoreguidelines-special-m std::uint32_t bits_ {}; +#ifdef BOOST_DECIMAL_DEBUG_ACCESSORS +public: +#endif // Returns the un-biased (quantum) exponent constexpr auto unbiased_exponent() const noexcept -> exponent_type ; @@ -153,6 +156,12 @@ BOOST_DECIMAL_EXPORT class decimal32 final // NOLINT(cppcoreguidelines-special-m constexpr auto full_significand() const noexcept -> significand_type ; constexpr auto isneg() const noexcept -> bool; + // Returns a complete struct so we don't have to decode multiple times + constexpr auto to_components() const noexcept -> detail::decimal32_components; + +#ifdef BOOST_DECIMAL_DEBUG_ACCESSORS +private: +#endif // Attempts conversion to integral type: // If this is nan sets errno to EINVAL and returns 0 // If this is not representable sets errno to ERANGE and returns 0 @@ -1444,6 +1453,55 @@ constexpr auto decimal32::full_significand() const noexcept -> significand_type return significand; } +constexpr auto decimal32::isneg() const noexcept -> bool +{ + return static_cast(bits_ & detail::d32_sign_mask); +} + +constexpr auto decimal32::to_components() const noexcept -> detail::decimal32_components +{ + detail::decimal32_components components {}; + + exponent_type expval {}; + significand_type significand {}; + + const auto comb_bits {(bits_ & detail::d32_comb_11_mask)}; + + switch (comb_bits) + { + case detail::d32_comb_11_mask: + // bits 2 and 3 are the exp part of the combination field + expval = (bits_ & detail::d32_comb_11_exp_bits) >> (detail::d32_significand_bits + 1); + // Only need the one bit of T because the other 3 are implied + significand = (bits_ & detail::d32_comb_11_significand_bits) == detail::d32_comb_11_significand_bits ? + UINT32_C(0b1001'0000000000'0000000000) : + UINT32_C(0b1000'0000000000'0000000000); + break; + case detail::d32_comb_10_mask: + expval = UINT32_C(0b10000000); + // Last three bits in the combination field, so we need to shift past the exp field + // which is next + significand |= (bits_ & detail::d32_comb_00_01_10_significand_bits) >> detail::d32_exponent_bits; + break; + case detail::d32_comb_01_mask: + expval = UINT32_C(0b01000000); + significand |= (bits_ & detail::d32_comb_00_01_10_significand_bits) >> detail::d32_exponent_bits; + break; + case 0U: + significand |= (bits_ & detail::d32_comb_00_01_10_significand_bits) >> detail::d32_exponent_bits; + break; + } + + significand |= (bits_ & detail::d32_significand_mask); + expval |= (bits_ & detail::d32_exponent_mask) >> detail::d32_significand_bits; + + components.sig = significand; + components.exp = static_cast(expval) - detail::bias_v; + components.sign = bits_ & detail::d32_sign_mask; + + return components; +} + template constexpr auto decimal32::edit_significand(T sig) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, T, void) @@ -1451,11 +1509,6 @@ constexpr auto decimal32::edit_significand(T sig) noexcept *this = decimal32(sig, this->biased_exponent(), this->isneg()); } -constexpr auto decimal32::isneg() const noexcept -> bool -{ - return static_cast(bits_ & detail::d32_sign_mask); -} - // Allows changing the sign even on nans and infs constexpr auto decimal32::edit_sign(bool sign) noexcept -> void { diff --git a/include/boost/decimal/detail/cmath/acosh.hpp b/include/boost/decimal/detail/cmath/acosh.hpp index 1a1176a6..797ceb6f 100644 --- a/include/boost/decimal/detail/cmath/acosh.hpp +++ b/include/boost/decimal/detail/cmath/acosh.hpp @@ -54,7 +54,7 @@ constexpr auto acosh_impl(T x) noexcept #endif else { - constexpr T one { 1, 0 }; + const T one { 1, 0 }; if (x < one) { diff --git a/include/boost/decimal/detail/cmath/asinh.hpp b/include/boost/decimal/detail/cmath/asinh.hpp index 118db4a2..e95fc54f 100644 --- a/include/boost/decimal/detail/cmath/asinh.hpp +++ b/include/boost/decimal/detail/cmath/asinh.hpp @@ -35,8 +35,8 @@ constexpr auto asinh_impl(T x) noexcept { // Use (parts of) the implementation of asinh from Boost.Math. - constexpr T zero { 0, 0 }; - constexpr T one { 1, 0 }; + const T zero { 0, 0 }; + const T one { 1, 0 }; if (x < zero) { diff --git a/include/boost/decimal/detail/cmath/atan.hpp b/include/boost/decimal/detail/cmath/atan.hpp index 26fd3edd..cc94cb53 100644 --- a/include/boost/decimal/detail/cmath/atan.hpp +++ b/include/boost/decimal/detail/cmath/atan.hpp @@ -53,7 +53,7 @@ constexpr auto atan_impl(T x) noexcept #endif else { - constexpr T one { 1 }; + const T one { 1 }; if (x <= T { 48 }) { diff --git a/include/boost/decimal/detail/cmath/atan2.hpp b/include/boost/decimal/detail/cmath/atan2.hpp index 144c3c9f..c73334b3 100644 --- a/include/boost/decimal/detail/cmath/atan2.hpp +++ b/include/boost/decimal/detail/cmath/atan2.hpp @@ -103,7 +103,7 @@ constexpr auto atan2_impl(T y, T x) noexcept } else if (fpcx == FP_INFINITE && !signx && isfinitey) { - constexpr T zero { 0, 0 }; + const T zero { 0, 0 }; result = signy ? -zero : zero; } diff --git a/include/boost/decimal/detail/cmath/atanh.hpp b/include/boost/decimal/detail/cmath/atanh.hpp index 7a97483b..8a9931ad 100644 --- a/include/boost/decimal/detail/cmath/atanh.hpp +++ b/include/boost/decimal/detail/cmath/atanh.hpp @@ -33,12 +33,12 @@ constexpr auto atanh_impl(T x) noexcept } else { - constexpr T zero { 0, 0 }; + const T zero { 0, 0 }; const auto b_neg = x < zero; const auto xx = abs(x); - constexpr T one { 1, 0 }; + const T one { 1, 0 }; if (xx > one) { diff --git a/include/boost/decimal/detail/cmath/ceil.hpp b/include/boost/decimal/detail/cmath/ceil.hpp index a82d3862..6a42aad6 100644 --- a/include/boost/decimal/detail/cmath/ceil.hpp +++ b/include/boost/decimal/detail/cmath/ceil.hpp @@ -29,8 +29,8 @@ constexpr auto ceil BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (T val) noexcept { using DivType = typename T::significand_type; - constexpr T zero {0, 0}; - constexpr T one {1, 0}; + const T zero {0, 0}; + const T one {1, 0}; const auto fp {fpclassify(val)}; switch (fp) diff --git a/include/boost/decimal/detail/cmath/cos.hpp b/include/boost/decimal/detail/cmath/cos.hpp index 304e2fd4..ef420795 100644 --- a/include/boost/decimal/detail/cmath/cos.hpp +++ b/include/boost/decimal/detail/cmath/cos.hpp @@ -107,7 +107,7 @@ constexpr auto cos_impl(T x) noexcept } else { - constexpr T one { 1 }; + const T one { 1 }; result = one; } diff --git a/include/boost/decimal/detail/cmath/cosh.hpp b/include/boost/decimal/detail/cmath/cosh.hpp index d2f067d1..be77b799 100644 --- a/include/boost/decimal/detail/cmath/cosh.hpp +++ b/include/boost/decimal/detail/cmath/cosh.hpp @@ -29,8 +29,8 @@ constexpr auto cosh_impl(T x) noexcept { const auto fpc = fpclassify(x); - constexpr T zero { 0, 0 }; - constexpr T one { 1, 0 }; + const T zero { 0, 0 }; + const T one { 1, 0 }; auto result = zero; diff --git a/include/boost/decimal/detail/cmath/ellint_1.hpp b/include/boost/decimal/detail/cmath/ellint_1.hpp index 96033fea..dc0b1e4e 100644 --- a/include/boost/decimal/detail/cmath/ellint_1.hpp +++ b/include/boost/decimal/detail/cmath/ellint_1.hpp @@ -34,7 +34,7 @@ template constexpr auto ellint_1_impl(T m, T phi) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T one { 1 }; + const T one { 1 }; T result { }; @@ -47,7 +47,7 @@ constexpr auto ellint_1_impl(T m, T phi) noexcept } else if((fpc_phi == FP_ZERO) && (fpc_m == FP_NORMAL)) { - constexpr T zero { 0 }; + const T zero { 0 }; result = zero; } @@ -132,7 +132,7 @@ template constexpr auto comp_ellint_1_impl(T m) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T one { 1 }; + const T one { 1 }; T result { }; @@ -152,7 +152,7 @@ constexpr auto comp_ellint_1_impl(T m) noexcept } else { - constexpr T zero { 0 }; + const T zero { 0 }; T Fpm { }; diff --git a/include/boost/decimal/detail/cmath/ellint_2.hpp b/include/boost/decimal/detail/cmath/ellint_2.hpp index a84e3555..86e46065 100644 --- a/include/boost/decimal/detail/cmath/ellint_2.hpp +++ b/include/boost/decimal/detail/cmath/ellint_2.hpp @@ -34,7 +34,7 @@ template constexpr auto ellint_2_impl(T m, T phi) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T one { 1 }; + const T one { 1 }; T result { }; @@ -47,7 +47,7 @@ constexpr auto ellint_2_impl(T m, T phi) noexcept } else if((fpc_phi == FP_ZERO) && (fpc_m == FP_NORMAL)) { - constexpr T zero { 0 }; + const T zero { 0 }; result = zero; } @@ -129,7 +129,7 @@ template constexpr auto comp_ellint_2_impl(T m) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T one { 1 }; + const T one { 1 }; T result { }; @@ -149,7 +149,7 @@ constexpr auto comp_ellint_2_impl(T m) noexcept } else { - constexpr T zero { 0 }; + const T zero { 0 }; T Fpm { }; T Km { }; diff --git a/include/boost/decimal/detail/cmath/erf.hpp b/include/boost/decimal/detail/cmath/erf.hpp index 417a0575..8e4f1b4a 100644 --- a/include/boost/decimal/detail/cmath/erf.hpp +++ b/include/boost/decimal/detail/cmath/erf.hpp @@ -102,7 +102,7 @@ constexpr auto erf_series_near_zero_sum(const T &x) noexcept -> T template constexpr auto erf_calc_impl(T z, bool invert) noexcept -> T { - constexpr T zero {0, 0}; + const T zero {0, 0}; if (z < zero) { @@ -232,7 +232,7 @@ constexpr auto erf_calc_impl(T z, bool invert) noexcept -> T T{UINT64_C(8978713707780316114), -22} }; - constexpr T one_and_half {15, -1}; + const T one_and_half {15, -1}; result = Y + tools::evaluate_polynomial(P, T(z - one_and_half)) / tools::evaluate_polynomial(Q, T(z - one_and_half)); } else if (z < T{45, -1}) diff --git a/include/boost/decimal/detail/cmath/exp.hpp b/include/boost/decimal/detail/cmath/exp.hpp index f80eb1b8..7af7f356 100644 --- a/include/boost/decimal/detail/cmath/exp.hpp +++ b/include/boost/decimal/detail/cmath/exp.hpp @@ -30,8 +30,8 @@ constexpr auto exp_impl(T x) noexcept { const auto fpc = fpclassify(x); - constexpr T zero { 0, 0 }; - constexpr T one { 1, 0 }; + const T zero { 0, 0 }; + const T one { 1, 0 }; auto result = zero; diff --git a/include/boost/decimal/detail/cmath/expm1.hpp b/include/boost/decimal/detail/cmath/expm1.hpp index 4c9b0b45..80580934 100644 --- a/include/boost/decimal/detail/cmath/expm1.hpp +++ b/include/boost/decimal/detail/cmath/expm1.hpp @@ -29,8 +29,8 @@ constexpr auto expm1_impl(T x) noexcept { const auto fpc = fpclassify(x); - constexpr T zero { 0, 0 }; - constexpr T one { 1, 0 }; + const T zero { 0, 0 }; + const T one { 1, 0 }; auto result = zero; diff --git a/include/boost/decimal/detail/cmath/fdim.hpp b/include/boost/decimal/detail/cmath/fdim.hpp index b70abc9b..b558ddf8 100644 --- a/include/boost/decimal/detail/cmath/fdim.hpp +++ b/include/boost/decimal/detail/cmath/fdim.hpp @@ -23,7 +23,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto fdim(T x, T y) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T zero {0, 0}; + const T zero {0, 0}; #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(x) || isinf(x)) diff --git a/include/boost/decimal/detail/cmath/floor.hpp b/include/boost/decimal/detail/cmath/floor.hpp index 909af9e2..a4f786de 100644 --- a/include/boost/decimal/detail/cmath/floor.hpp +++ b/include/boost/decimal/detail/cmath/floor.hpp @@ -29,7 +29,7 @@ constexpr auto floor BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (T val) noexcept { using DivType = typename T::significand_type; - constexpr T zero {0, 0}; + const T zero {0, 0}; constexpr T neg_one {1, 0, true}; const auto fp {fpclassify(val)}; diff --git a/include/boost/decimal/detail/cmath/fpclassify.hpp b/include/boost/decimal/detail/cmath/fpclassify.hpp index d75f0970..ea044607 100644 --- a/include/boost/decimal/detail/cmath/fpclassify.hpp +++ b/include/boost/decimal/detail/cmath/fpclassify.hpp @@ -23,7 +23,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto fpclassify BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (T rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, T, int) { - constexpr T zero {0, 0}; + const T zero {0, 0}; #ifdef BOOST_DECIMAL_FAST_MATH diff --git a/include/boost/decimal/detail/cmath/frexp.hpp b/include/boost/decimal/detail/cmath/frexp.hpp index ae454ec2..b0ff0cb3 100644 --- a/include/boost/decimal/detail/cmath/frexp.hpp +++ b/include/boost/decimal/detail/cmath/frexp.hpp @@ -28,7 +28,7 @@ constexpr auto frexp_impl(T v, int* expon) noexcept { // This implementation of frexp follows closely that of eval_frexp // in Boost.Multiprecision's cpp_dec_float template class. - constexpr T zero { 0, 0 }; + const T zero { 0, 0 }; auto result_frexp = zero; diff --git a/include/boost/decimal/detail/cmath/frexp10.hpp b/include/boost/decimal/detail/cmath/frexp10.hpp index 76339bae..ddcbd0aa 100644 --- a/include/boost/decimal/detail/cmath/frexp10.hpp +++ b/include/boost/decimal/detail/cmath/frexp10.hpp @@ -28,7 +28,7 @@ namespace decimal { BOOST_DECIMAL_EXPORT template constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type { - constexpr T zero {0, 0}; + const T zero {0, 0}; if (num == zero) { diff --git a/include/boost/decimal/detail/cmath/hypot.hpp b/include/boost/decimal/detail/cmath/hypot.hpp index cb354a37..c5744e36 100644 --- a/include/boost/decimal/detail/cmath/hypot.hpp +++ b/include/boost/decimal/detail/cmath/hypot.hpp @@ -28,7 +28,7 @@ namespace detail { template constexpr auto hypot_impl(T x, T y) noexcept { - constexpr T zero {0, 0}; + const T zero {0, 0}; if (abs(x) == zero #ifndef BOOST_DECIMAL_FAST_MATH diff --git a/include/boost/decimal/detail/cmath/impl/ellint_impl.hpp b/include/boost/decimal/detail/cmath/impl/ellint_impl.hpp index 5d168e2f..d63f0b68 100644 --- a/include/boost/decimal/detail/cmath/impl/ellint_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/ellint_impl.hpp @@ -67,7 +67,7 @@ constexpr auto agm(T phi, const bool phi_is_pi_half { phi == my_pi_half }; - constexpr T one { 1 }; + const T one { 1 }; const bool has_e { ((pEm != nullptr) || (pEpm != nullptr)) }; @@ -94,7 +94,7 @@ constexpr auto agm(T phi, } else { - constexpr T zero { 0 }; + const T zero { 0 }; constexpr T half { 5 , -1 }; T a0 { one }; diff --git a/include/boost/decimal/detail/cmath/impl/pow_impl.hpp b/include/boost/decimal/detail/cmath/impl/pow_impl.hpp index 30a1700e..d990068d 100644 --- a/include/boost/decimal/detail/cmath/impl/pow_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/pow_impl.hpp @@ -22,7 +22,7 @@ constexpr auto pow_n_impl(T b, UnsignedIntegralType p) noexcept -> std::enable_i { using local_unsigned_integral_type = UnsignedIntegralType; - constexpr T one { 1, 0 }; + const T one { 1, 0 }; T result { }; @@ -78,7 +78,7 @@ constexpr auto pow_n_impl(T b, UnsignedIntegralType p) noexcept -> std::enable_i template constexpr auto pow_2_impl(int e2) noexcept -> std::enable_if_t, T> { - constexpr T one { 1, 0 }; + const T one { 1, 0 }; T result { }; diff --git a/include/boost/decimal/detail/cmath/lgamma.hpp b/include/boost/decimal/detail/cmath/lgamma.hpp index af53ed2a..e21479ac 100644 --- a/include/boost/decimal/detail/cmath/lgamma.hpp +++ b/include/boost/decimal/detail/cmath/lgamma.hpp @@ -66,13 +66,13 @@ constexpr auto lgamma_impl(T x) noexcept } else if ((is_pure_int) && ((nx == 1) || (nx == 2))) { - constexpr T zero { 0, 0 }; + const T zero { 0, 0 }; result = zero; } else { - constexpr T one { 1, 0 }; + const T one { 1, 0 }; if (signbit(x)) { diff --git a/include/boost/decimal/detail/cmath/log.hpp b/include/boost/decimal/detail/cmath/log.hpp index 87d25d41..5f3d580a 100644 --- a/include/boost/decimal/detail/cmath/log.hpp +++ b/include/boost/decimal/detail/cmath/log.hpp @@ -26,7 +26,7 @@ template constexpr auto log_impl(T x) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T one { 1, 0 }; + const T one { 1, 0 }; T result { }; @@ -66,7 +66,7 @@ constexpr auto log_impl(T x) noexcept } else { - constexpr T zero { 0 }; + const T zero { 0 }; result = zero; } diff --git a/include/boost/decimal/detail/cmath/log10.hpp b/include/boost/decimal/detail/cmath/log10.hpp index 97100d6d..594378f7 100644 --- a/include/boost/decimal/detail/cmath/log10.hpp +++ b/include/boost/decimal/detail/cmath/log10.hpp @@ -74,7 +74,7 @@ constexpr auto log10_impl(T x) noexcept } else { - constexpr T one { 1 }; + const T one { 1 }; if (x < one) { diff --git a/include/boost/decimal/detail/cmath/log1p.hpp b/include/boost/decimal/detail/cmath/log1p.hpp index 58de2216..e7e9c50c 100644 --- a/include/boost/decimal/detail/cmath/log1p.hpp +++ b/include/boost/decimal/detail/cmath/log1p.hpp @@ -27,7 +27,7 @@ template constexpr auto log1p_impl(T x) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T one { 1, 0 }; + const T one { 1, 0 }; T result { }; diff --git a/include/boost/decimal/detail/cmath/modf.hpp b/include/boost/decimal/detail/cmath/modf.hpp index a799e5d9..3789c3f0 100644 --- a/include/boost/decimal/detail/cmath/modf.hpp +++ b/include/boost/decimal/detail/cmath/modf.hpp @@ -25,7 +25,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto modf(T x, T* iptr) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T zero {0, 0}; + const T zero {0, 0}; const auto is_neg {x < zero}; if (abs(x) == zero || isinf(x)) diff --git a/include/boost/decimal/detail/cmath/pow.hpp b/include/boost/decimal/detail/cmath/pow.hpp index e9dd2ba4..c8a6cb6d 100644 --- a/include/boost/decimal/detail/cmath/pow.hpp +++ b/include/boost/decimal/detail/cmath/pow.hpp @@ -27,8 +27,8 @@ constexpr auto pow(T b, IntegralType p) noexcept { using local_integral_type = IntegralType; - constexpr T zero { 0, 0 }; - constexpr T one { 1, 0 }; + const T zero { 0, 0 }; + const T one { 1, 0 }; T result { }; @@ -150,7 +150,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto pow(T x, T a) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T zero { 0, 0 }; + const T zero { 0, 0 }; auto result = zero; @@ -162,7 +162,7 @@ constexpr auto pow(T x, T a) noexcept } else { - constexpr T one { 1, 0 }; + const T one { 1, 0 }; const auto fpc_x = fpclassify(x); const auto fpc_a = fpclassify(a); diff --git a/include/boost/decimal/detail/cmath/remainder.hpp b/include/boost/decimal/detail/cmath/remainder.hpp index 8ed8a4d6..e838e664 100644 --- a/include/boost/decimal/detail/cmath/remainder.hpp +++ b/include/boost/decimal/detail/cmath/remainder.hpp @@ -23,7 +23,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto remainder(T x, T y) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T zero {0, 0}; + const T zero {0, 0}; constexpr T half {5, -1}; #ifndef BOOST_DECIMAL_FAST_MATH diff --git a/include/boost/decimal/detail/cmath/remquo.hpp b/include/boost/decimal/detail/cmath/remquo.hpp index 783aec92..fad6c8bf 100644 --- a/include/boost/decimal/detail/cmath/remquo.hpp +++ b/include/boost/decimal/detail/cmath/remquo.hpp @@ -27,7 +27,7 @@ constexpr auto remquo(T x, T y, int* quo) noexcept using unsigned_significand_type = std::conditional_t::value || std::is_same::value, detail::uint128, std::uint64_t>; - constexpr T zero {0, 0}; + const T zero {0, 0}; constexpr T half {5, -1}; #ifndef BOOST_DECIMAL_FAST_MATH diff --git a/include/boost/decimal/detail/cmath/riemann_zeta.hpp b/include/boost/decimal/detail/cmath/riemann_zeta.hpp index 21915d65..9f0e703f 100644 --- a/include/boost/decimal/detail/cmath/riemann_zeta.hpp +++ b/include/boost/decimal/detail/cmath/riemann_zeta.hpp @@ -32,7 +32,7 @@ constexpr auto riemann_zeta_impl(T x) noexcept { const auto fpc = fpclassify(x); - constexpr T one { 1, 0 }; + const T one { 1, 0 }; const bool is_neg { signbit(x) }; diff --git a/include/boost/decimal/detail/cmath/rint.hpp b/include/boost/decimal/detail/cmath/rint.hpp index 831f6104..92f885f0 100644 --- a/include/boost/decimal/detail/cmath/rint.hpp +++ b/include/boost/decimal/detail/cmath/rint.hpp @@ -51,7 +51,7 @@ constexpr auto rint_impl(T1& sig, T2 exp, bool sign) template constexpr auto lrint_impl(T num) noexcept -> Int { - constexpr T zero {0, 0}; + const T zero {0, 0}; constexpr T lmax {(std::numeric_limits::max)()}; constexpr T lmin {(std::numeric_limits::min)()}; @@ -116,7 +116,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto rint(T num) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T zero {0, 0}; + const T zero {0, 0}; constexpr T max_round_value {1 / std::numeric_limits::epsilon()}; #ifndef BOOST_DECIMAL_FAST_MATH diff --git a/include/boost/decimal/detail/cmath/round.hpp b/include/boost/decimal/detail/cmath/round.hpp index 4d78a0b3..7368e436 100644 --- a/include/boost/decimal/detail/cmath/round.hpp +++ b/include/boost/decimal/detail/cmath/round.hpp @@ -25,7 +25,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto round(T num) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T zero {0, 0}; + const T zero {0, 0}; constexpr T half {5, -1}; #ifndef BOOST_DECIMAL_FAST_MATH @@ -67,7 +67,7 @@ namespace detail { template constexpr auto int_round_impl(T num) noexcept -> Int { - constexpr T zero {0, 0}; + const T zero {0, 0}; constexpr T lmax {(std::numeric_limits::max)()}; constexpr T lmin {(std::numeric_limits::min)()}; diff --git a/include/boost/decimal/detail/cmath/sin.hpp b/include/boost/decimal/detail/cmath/sin.hpp index d3aea106..00dce2aa 100644 --- a/include/boost/decimal/detail/cmath/sin.hpp +++ b/include/boost/decimal/detail/cmath/sin.hpp @@ -71,7 +71,7 @@ constexpr auto sin_impl(T x) noexcept T r { two_r / 2 }; - constexpr T one { 1 }; + const T one { 1 }; bool do_scaling { two_r > one }; diff --git a/include/boost/decimal/detail/cmath/sinh.hpp b/include/boost/decimal/detail/cmath/sinh.hpp index 98b165a9..4e34a89c 100644 --- a/include/boost/decimal/detail/cmath/sinh.hpp +++ b/include/boost/decimal/detail/cmath/sinh.hpp @@ -29,7 +29,7 @@ constexpr auto sinh_impl(T x) noexcept { const auto fpc = fpclassify(x); - constexpr T zero { 0, 0 }; + const T zero { 0, 0 }; auto result = zero; @@ -58,7 +58,7 @@ constexpr auto sinh_impl(T x) noexcept } else { - constexpr T one { 1, 0 }; + const T one { 1, 0 }; if (x < one) { diff --git a/include/boost/decimal/detail/cmath/sqrt.hpp b/include/boost/decimal/detail/cmath/sqrt.hpp index ac17096f..7ff7b0c2 100644 --- a/include/boost/decimal/detail/cmath/sqrt.hpp +++ b/include/boost/decimal/detail/cmath/sqrt.hpp @@ -63,7 +63,7 @@ constexpr auto sqrt_impl(T x) noexcept const bool is_pure { static_cast(zeros_removal.trimmed_number) == 1U }; - constexpr T one { 1 }; + const T one { 1 }; if(is_pure) { diff --git a/include/boost/decimal/detail/cmath/tan.hpp b/include/boost/decimal/detail/cmath/tan.hpp index 5ec3e2d3..34f48a8c 100644 --- a/include/boost/decimal/detail/cmath/tan.hpp +++ b/include/boost/decimal/detail/cmath/tan.hpp @@ -76,7 +76,7 @@ constexpr auto tan(T x) noexcept constexpr T cbrt_epsilon { cbrt(std::numeric_limits::epsilon()) }; - constexpr T one { 1 }; + const T one { 1 }; constexpr T two { 2 }; switch(n) diff --git a/include/boost/decimal/detail/cmath/tanh.hpp b/include/boost/decimal/detail/cmath/tanh.hpp index 940d7a3a..a1564f78 100644 --- a/include/boost/decimal/detail/cmath/tanh.hpp +++ b/include/boost/decimal/detail/cmath/tanh.hpp @@ -29,8 +29,8 @@ constexpr auto tanh_impl(T x) noexcept { const auto fpc = fpclassify(x); - constexpr T zero { 0, 0 }; - constexpr T one { 1, 0 }; + const T zero { 0, 0 }; + const T one { 1, 0 }; auto result = zero; diff --git a/include/boost/decimal/detail/cmath/tgamma.hpp b/include/boost/decimal/detail/cmath/tgamma.hpp index 35a5c140..b7ce9270 100644 --- a/include/boost/decimal/detail/cmath/tgamma.hpp +++ b/include/boost/decimal/detail/cmath/tgamma.hpp @@ -73,7 +73,7 @@ constexpr auto tgamma_impl(T x) noexcept } else { - constexpr T one { 1, 0 }; + const T one { 1, 0 }; if (is_pure_int) { diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index 02ad865c..43b3e524 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -341,6 +341,12 @@ typedef unsigned __int128 uint128_t; # endif # endif +#ifdef __has_cpp_attribute +# if __has_cpp_attribute(nodiscard) +# define BOOST_DECIMAL_NO_DISCARD [[nodiscard]] +# endif +#endif + #endif // Since we should not be able to pull these in from the STL in module mode define them ourselves @@ -354,4 +360,8 @@ typedef unsigned __int128 uint128_t; # endif #endif +#if __has_include() && defined(_GLIBCXX_USE_DECIMAL_FLOAT) && defined(__GNUC__) && __GNUC__ >= 7 +# define BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL +#endif + #endif // BOOST_DECIMAL_DETAIL_CONFIG_HPP diff --git a/include/boost/decimal/detail/to_integral.hpp b/include/boost/decimal/detail/to_integral.hpp index 203b3237..21fe1e58 100644 --- a/include/boost/decimal/detail/to_integral.hpp +++ b/include/boost/decimal/detail/to_integral.hpp @@ -35,8 +35,8 @@ constexpr auto to_integral(Decimal val) noexcept { using Conversion_Type = std::conditional_t::is_signed, std::int64_t, std::uint64_t>; - constexpr Decimal max_target_type { (std::numeric_limits::max)() }; - constexpr Decimal min_target_type { (std::numeric_limits::min)() }; + const Decimal max_target_type { (std::numeric_limits::max)() }; + const Decimal min_target_type { (std::numeric_limits::min)() }; if (isnan(val)) { diff --git a/include/boost/decimal/detail/type_traits.hpp b/include/boost/decimal/detail/type_traits.hpp index 06a32846..86bcc2e9 100644 --- a/include/boost/decimal/detail/type_traits.hpp +++ b/include/boost/decimal/detail/type_traits.hpp @@ -7,6 +7,7 @@ // Extends the current type traits to include our types and __int128s +#include #include #include @@ -153,12 +154,33 @@ struct is_decimal_floating_point { static constexpr bool value = template <> struct is_decimal_floating_point { static constexpr bool value = true; }; +#ifdef BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL +template <> +struct is_decimal_floating_point { static constexpr bool value = true; }; +#endif + template constexpr bool is_decimal_floating_point::value; template constexpr bool is_decimal_floating_point_v = is_decimal_floating_point::value; +#ifdef BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL + +template +struct is_wrapper_type { static constexpr bool value = false; }; + +template <> +struct is_wrapper_type { static constexpr bool value = true; }; + +template +constexpr bool is_wrapper_type::value; + +template +constexpr bool is_wrapper_type_v = is_wrapper_type::value; + +#endif + template struct conjunction : std::true_type {}; diff --git a/include/boost/decimal/fwd.hpp b/include/boost/decimal/fwd.hpp index 67583524..d339fbb5 100644 --- a/include/boost/decimal/fwd.hpp +++ b/include/boost/decimal/fwd.hpp @@ -20,6 +20,10 @@ class decimal64_fast; class decimal128; class decimal128_fast; +#ifdef BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL +class gcc_decimal32; +#endif + } // namespace decimal } // namespace boost diff --git a/include/boost/decimal/gcc_decimal32.hpp b/include/boost/decimal/gcc_decimal32.hpp new file mode 100644 index 00000000..b0737a98 --- /dev/null +++ b/include/boost/decimal/gcc_decimal32.hpp @@ -0,0 +1,757 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_GCC_DECIMAL32_HPP +#define BOOST_DECIMAL_GCC_DECIMAL32_HPP + +#include + +#ifdef BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL + +#include +#include +#include + +#ifndef BOOST_DECIMAL_BUILD_MODULE + +#include +#include + +#endif // BOOST_DECIMAL_BUILD_MODULE + +#define BOOST_DECIMAL_HAS_GCC_DECIMAL32 1 + +namespace boost { +namespace decimal { + +namespace detail { + +// Masks to update the significand based on the combination field +// GCC / IBM do not follow the IEEE 754 encoding standard for BID +// Reverse engineering found the following patters: +// +// Comb. Exponent Significand +// s eeeeeeee ttttttttttttttttttttttt - sign + 2 steering bits concatenate to 6 bits of exponent + 23 bits of significand like float +// s 11 eeeeeeee [t]ttttttttttttttttttttt - sign + 2 steering bits + 8 bits of exponent + 21 bits of significand +// +// Only is the type different in steering 11 which yields significand 100 + 21 bits giving us our 24 total bits of precision + +#ifdef BOOST_DECIMAL_ENDIAN_LITTLE_BYTE + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_sign_mask = UINT32_C(0b10000000000000000000000000000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_steering_bits_mask = UINT32_C(0b01100000000000000000000000000000); + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_00_steering_bits = UINT32_C(0); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_01_steering_bits = UINT32_C(0b00100000000000000000000000000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_10_steering_bits = UINT32_C(0b01000000000000000000000000000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_11_steering_bits = gccd32_steering_bits_mask; + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_01_exp_mask = UINT32_C(0b01111111100000000000000000000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_11_exp_mask = UINT32_C(0b00011111111000000000000000000000); + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_01_significand_mask = UINT32_C(0b00000000011111111111111111111111); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_11_significand_mask = UINT32_C(0b00000000000111111111111111111111); + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_inf_mask = UINT32_C(0x78000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_qnan_mask = UINT32_C(0x7C000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_snan_mask = UINT32_C(0x7E000000); + +#else + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_sign_mask = UINT32_C(0b00000000000000000000000000000001); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_steering_bits_mask = UINT32_C(0b00000000000000000000000000000110); + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_00_steering_bits = UINT32_C(0); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_01_steering_bits = UINT32_C(0b00000000000000000000000000000100); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_10_steering_bits = UINT32_C(0b00000000000000000000000000000010); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_11_steering_bits = gccd32_steering_bits_mask; + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_01_exp_mask = UINT32_C(0b00000000000000000000000111111110); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_11_exp_mask = UINT32_C(0b00000000000000000000011111111000); + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_01_significand_mask = UINT32_C(0b11111111111111111111111000000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_11_significand_mask = UINT32_C(0b11111111111111111111100000000000); + +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_inf_mask = UINT32_C(0b00000000000000000000000000011110); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_qnan_mask = UINT32_C(0b00000000000000000000000000111110); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_snan_mask = UINT32_C(0b00000000000000000000000001111110); + +#endif + +// Non-finite values +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_inf = UINT32_C(0x78000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_qnan = UINT32_C(0x7C000000); +BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t gccd32_snan = UINT32_C(0x7E000000); + +} // namespace detail + +// This type is a wrapper around gcc std::decimal::decimal32 to allow it to use +// the standard library provided by Boost.Decimal +BOOST_DECIMAL_EXPORT class gcc_decimal32 final +{ +public: + using significand_type = std::uint32_t; + using exponent_type = std::uint32_t; + using biased_exponent_type = std::int32_t; + +private: + + static_assert(sizeof(std::decimal::decimal32) == sizeof(std::uint32_t), "Incorrect size detected. std::decimal::decimal32 must be exactly 32 bits"); + + std::decimal::decimal32 internal_decimal_ {}; + +#ifdef BOOST_DECIMAL_DEBUG_ACCESSORS +public: +#endif + // Returns the un-biased (quantum) exponent + inline auto unbiased_exponent() const noexcept -> exponent_type; + + // Returns the biased exponent + inline auto biased_exponent() const noexcept -> biased_exponent_type; + + // Returns the significand complete with the bits implied from the combination field + inline auto full_significand() const noexcept -> significand_type; + + inline auto isneg() const noexcept -> bool; + + // Since we have the overhead of memcpy in order to decode anything + // get them all at once if we are going to need it + inline auto to_components() const noexcept -> detail::decimal32_components; + + inline auto underlying() const noexcept -> std::decimal::decimal32 { return internal_decimal_; } + + // cmath functions that are easier as friends + friend inline auto signbit BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool; + friend inline auto isinf BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool; + friend inline auto isnan BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool; + friend inline auto issignaling BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool; + friend inline auto isnormal BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool; + friend inline auto isfinite BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool; + + // Attempts conversion to integral type: + // If this is nan sets errno to EINVAL and returns 0 + // If this is not representable sets errno to ERANGE and returns 0 + template + friend constexpr auto to_integral(Decimal val) noexcept + BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_integral_v, TargetType, TargetType); + + template + friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type; + +public: + + gcc_decimal32() = default; + + explicit gcc_decimal32(std::decimal::decimal32 decimal) { internal_decimal_ = decimal; } + + // 3.2.5 Initialization from coefficient and exponent. + template && detail::is_signed_v, bool> = true> + gcc_decimal32(Integer coeff) + { + internal_decimal_ = std::decimal::make_decimal32(static_cast(coeff), 0); + } + + template && detail::is_unsigned_v, bool> = true> + gcc_decimal32(Integer coeff) + { + internal_decimal_ = std::decimal::make_decimal32(static_cast(coeff), 0); + } + + template && detail::is_signed_v, bool> = true> + gcc_decimal32(Integer coeff, int exp) + { + internal_decimal_ = std::decimal::make_decimal32(static_cast(coeff), exp); + } + + template && detail::is_unsigned_v, bool> = true> + gcc_decimal32(Integer coeff, int exp) + { + internal_decimal_ = std::decimal::make_decimal32(static_cast(coeff), exp); + } + + // Non-conforming extension: Conversion to integral type. + inline operator unsigned long long() const noexcept; + inline operator long long() const noexcept; + inline operator unsigned long() const noexcept; + inline operator long() const noexcept; + inline operator unsigned() const noexcept; + inline operator int() const noexcept; + + // 3.2.6 Conversion to generic floating-point type. + inline explicit operator float() const noexcept; + inline explicit operator double() const noexcept; + inline explicit operator long double() const noexcept; + + // 3.2.7 Unary arithmetic operators. + friend gcc_decimal32 operator+(const gcc_decimal32 rhs) { return gcc_decimal32{rhs.underlying()}; } + friend gcc_decimal32 operator-(const gcc_decimal32 rhs) { return gcc_decimal32{-rhs.underlying()}; } + + // 3.2.8 Binary arithmetic operators. + friend auto operator+(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> gcc_decimal32 + {return gcc_decimal32{lhs.underlying() + rhs.underlying()}; } + + template + friend auto operator+(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, gcc_decimal32) + { return gcc_decimal32{lhs.underlying() + rhs}; } + + template + friend auto operator+(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, gcc_decimal32) + { return gcc_decimal32{lhs + rhs.underlying()}; } + + friend auto operator-(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> gcc_decimal32 + {return gcc_decimal32{lhs.underlying() - rhs.underlying()}; } + + template + friend auto operator-(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, gcc_decimal32) + { return gcc_decimal32{lhs.underlying() - rhs}; } + + template + friend auto operator-(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, gcc_decimal32) + { return gcc_decimal32{lhs - rhs.underlying()}; } + + friend auto operator*(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> gcc_decimal32 + { return gcc_decimal32{lhs.underlying() * rhs.underlying()}; } + + template + friend auto operator*(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, gcc_decimal32) + { return gcc_decimal32{lhs.underlying() * rhs}; } + + template + friend auto operator*(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, gcc_decimal32) + { return gcc_decimal32{lhs * rhs.underlying()}; } + + friend auto operator/(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> gcc_decimal32 + {return gcc_decimal32{lhs.underlying() / rhs.underlying()}; } + + template + friend auto operator/(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, gcc_decimal32) + { return gcc_decimal32{lhs.underlying() / rhs}; } + + template + friend auto operator/(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, gcc_decimal32) + { return gcc_decimal32{lhs / rhs.underlying()}; } + + // 3.2.9 Comparison operators. + friend auto operator==(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> bool + { return lhs.underlying() == rhs.underlying(); } + + template + friend auto operator==(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs.underlying() == rhs; } + + template + friend auto operator==(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs == rhs.underlying(); } + + friend auto operator!=(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> bool + { return lhs.underlying() != rhs.underlying(); } + + template + friend auto operator!=(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs.underlying() != rhs; } + + template + friend auto operator!=(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs != rhs.underlying(); } + + friend auto operator<(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> bool + {return lhs.underlying() < rhs.underlying(); } + + template + friend auto operator<(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs.underlying() < rhs; } + + template + friend auto operator<(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs < rhs.underlying(); } + + friend auto operator<=(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> bool + { return lhs.underlying() <= rhs.underlying(); } + + template + friend auto operator<=(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs.underlying() <= rhs; } + + template + friend auto operator<=(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs <= rhs.underlying(); } + + friend auto operator>(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> bool + { return lhs.underlying() > rhs.underlying(); } + + template + friend auto operator>(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs.underlying() > rhs; } + + template + friend auto operator>(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs > rhs.underlying(); } + + friend auto operator>=(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> bool + { return lhs.underlying() >= rhs.underlying(); } + + template + friend auto operator>=(gcc_decimal32 lhs, Integral rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs.underlying() >= rhs; } + + template + friend auto operator>=(Integral lhs, gcc_decimal32 rhs) + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integral, bool) + { return lhs >= rhs.underlying(); } + + #ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + friend constexpr auto operator<=>(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> std::partial_ordering; + + template + friend constexpr auto operator<=>(gcc_decimal32 lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering); + + template + friend constexpr auto operator<=>(Integer lhs, gcc_decimal32 rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering); + #endif + + // 3.2.2.6 Increment and decrement operators + inline auto operator++() noexcept -> gcc_decimal32&; + inline auto operator++(int) noexcept -> gcc_decimal32; + inline auto operator--() noexcept -> gcc_decimal32&; + inline auto operator--(int) noexcept -> gcc_decimal32; + + // 3.2.2.7 Compound assignment. + inline auto operator+=(gcc_decimal32 rhs) noexcept -> gcc_decimal32& + { internal_decimal_ += rhs.underlying(); return *this; } + + template + inline auto operator+=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, gcc_decimal32&) + { internal_decimal_ += rhs; return *this; } + + inline auto operator-=(gcc_decimal32 rhs) noexcept -> gcc_decimal32& + { internal_decimal_ -= rhs.underlying(); return *this; } + + template + inline auto operator-=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, gcc_decimal32&) + { internal_decimal_ -= rhs; return *this; } + + template + inline auto operator*=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, gcc_decimal32&) + { internal_decimal_ *= rhs; return *this; } + + inline auto operator*=(gcc_decimal32 rhs) noexcept -> gcc_decimal32& + { internal_decimal_ *= rhs.underlying(); return *this; } + + template + inline auto operator/=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, gcc_decimal32&) + { internal_decimal_ /= rhs; return *this; } + + inline auto operator/=(gcc_decimal32 rhs) noexcept -> gcc_decimal32& + { internal_decimal_ /= rhs.underlying(); return *this; } +}; + +template +auto operator<<(std::basic_ostream& os, const std::decimal::decimal32& d) -> std::basic_ostream& +{ + return os << gcc_decimal32{d}; +} + +namespace detail { + +inline auto decode_gccd32_unbiased_exponent(std::uint32_t bits_) noexcept -> gcc_decimal32::exponent_type +{ + gcc_decimal32::exponent_type expval {}; + const auto steering_bits {(bits_ & gccd32_steering_bits_mask)}; + + // 3 of the 4 steering patterns leave this laid out like a binary float instead of an IEEE 754 decimal float + if (steering_bits != gccd32_11_steering_bits) + { + expval |= (bits_ & gccd32_01_exp_mask) >> 23U; + } + else + { + expval |= (bits_ & gccd32_11_exp_mask) >> 21U; + } + + return expval; +} + +inline auto decode_gccd32_biased_exponent(std::uint32_t bits) -> gcc_decimal32::biased_exponent_type +{ + return static_cast(decode_gccd32_unbiased_exponent(bits)) - detail::bias_v; +} + +inline auto decode_gccd32_significand(std::uint32_t bits_) -> gcc_decimal32::significand_type +{ + gcc_decimal32::significand_type significand {}; + + const auto steering_bits {(bits_ & gccd32_steering_bits_mask)}; + + if (steering_bits != gccd32_11_steering_bits) + { + significand |= (bits_ & gccd32_01_significand_mask); + } + else + { + significand = UINT32_C(0b1000'0000000000'0000000000); + significand |= (bits_ & gccd32_11_significand_mask); + } + + return significand; +} + +inline auto decode_gccd32_sign(std::uint32_t bits_) -> bool +{ + return static_cast(bits_ & gccd32_sign_mask); +} + +} // detail + +inline auto gcc_decimal32::unbiased_exponent() const noexcept -> exponent_type +{ + std::uint32_t bits_; + std::memcpy(&bits_, &internal_decimal_, sizeof(std::uint32_t)); + return detail::decode_gccd32_unbiased_exponent(bits_); +} + +inline auto gcc_decimal32::biased_exponent() const noexcept -> biased_exponent_type +{ + std::uint32_t bits_; + std::memcpy(&bits_, &internal_decimal_, sizeof(std::uint32_t)); + return detail::decode_gccd32_biased_exponent(bits_); +} + +inline auto gcc_decimal32::full_significand() const noexcept -> significand_type +{ + std::uint32_t bits_ {}; + std::memcpy(&bits_, &internal_decimal_, sizeof(std::uint32_t)); + return detail::decode_gccd32_significand(bits_); +} + +inline auto gcc_decimal32::isneg() const noexcept -> bool +{ + std::uint32_t bits_ {}; + std::memcpy(&bits_, &internal_decimal_, sizeof(std::uint32_t)); + return detail::decode_gccd32_sign(bits_); +} + +inline auto gcc_decimal32::to_components() const noexcept -> detail::decimal32_components +{ + detail::decimal32_components components {}; + std::uint32_t bits_ {}; + std::memcpy(&bits_, &internal_decimal_, sizeof(std::uint32_t)); + + gcc_decimal32::exponent_type expval {}; + gcc_decimal32::significand_type significand {}; + const auto steering_bits {(bits_ & detail::gccd32_steering_bits_mask)}; + + if (steering_bits != detail::gccd32_11_steering_bits) + { + significand |= (bits_ & detail::gccd32_01_significand_mask); + expval |= (bits_ & detail::gccd32_01_exp_mask) >> 23U; + } + else + { + significand = UINT32_C(0b1000'0000000000'0000000000); + significand |= (bits_ & detail::gccd32_11_significand_mask); + expval |= (bits_ & detail::gccd32_11_exp_mask) >> 21U; + } + + components.sig = significand; + components.exp = static_cast(expval) - detail::bias_v; + components.sign = bits_ & detail::gccd32_sign_mask; + + return components; +} + +inline auto signbit BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool +{ + return rhs.isneg(); +} + +inline auto isinf BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool +{ + std::uint32_t bits_ {}; + std::memcpy(&bits_, &rhs.internal_decimal_, sizeof(std::uint32_t)); + + return bits_ == detail::gccd32_inf; +} + +inline auto isnan BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool +{ + std::uint32_t bits_ {}; + std::memcpy(&bits_, &rhs.internal_decimal_, sizeof(std::uint32_t)); + + return bits_ >= detail::gccd32_qnan; +} + +inline auto issignaling BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool +{ + std::uint32_t bits_ {}; + std::memcpy(&bits_, &rhs.internal_decimal_, sizeof(std::uint32_t)); + + return bits_ == detail::gccd32_snan; +} + +inline auto isfinite BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool +{ + std::uint32_t bits_ {}; + std::memcpy(&bits_, &rhs.internal_decimal_, sizeof(std::uint32_t)); + + return bits_ < detail::gccd32_inf; +} + +inline auto isnormal BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (gcc_decimal32 rhs) noexcept -> bool +{ + // Check for de-normals + const auto sig {rhs.full_significand()}; + const auto exp {rhs.unbiased_exponent()}; + + if (exp <= detail::precision_v - 1) + { + return false; + } + + return (sig != 0) && isfinite(rhs); +} + +inline gcc_decimal32::operator unsigned long long() const noexcept +{ + return to_integral(*this); +} + +inline gcc_decimal32::operator long long() const noexcept +{ + return std::decimal::decimal32_to_long_long(internal_decimal_); +} + +inline gcc_decimal32::operator unsigned long() const noexcept +{ + return to_integral(*this); +} + +inline gcc_decimal32::operator long() const noexcept +{ + return static_cast(std::decimal::decimal32_to_long_long(internal_decimal_)); +} + +inline gcc_decimal32::operator unsigned() const noexcept +{ + return static_cast(std::decimal::decimal32_to_long_long(internal_decimal_)); +} + +inline gcc_decimal32::operator int() const noexcept +{ + return static_cast(std::decimal::decimal32_to_long_long(internal_decimal_)); +} + +inline gcc_decimal32::operator float() const noexcept +{ + return std::decimal::decimal32_to_float(internal_decimal_); +} + +inline gcc_decimal32::operator double() const noexcept +{ + return std::decimal::decimal32_to_double(internal_decimal_); +} + +inline gcc_decimal32::operator long double() const noexcept +{ + return std::decimal::decimal32_to_long_double(internal_decimal_); +} + +#ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + +constexpr auto operator<=>(gcc_decimal32 lhs, gcc_decimal32 rhs) noexcept -> std::partial_ordering +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +template +constexpr auto operator<=>(gcc_decimal32 lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering) +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +template +constexpr auto operator<=>(Integer lhs, gcc_decimal32 rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering) +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +#endif + +inline auto gcc_decimal32::operator++() noexcept -> gcc_decimal32& +{ + internal_decimal_++; + return *this; +} + +inline auto gcc_decimal32::operator++(int) noexcept -> gcc_decimal32 +{ + internal_decimal_++; + return *this; +} + +inline auto gcc_decimal32::operator--() noexcept -> gcc_decimal32& +{ + internal_decimal_--; + return *this; +} + +inline auto gcc_decimal32::operator--(int) noexcept -> gcc_decimal32 +{ + internal_decimal_--; + return *this; +} + +} // namespace decimal +} // namespace boost + +BOOST_DECIMAL_EXPORT template <> +struct std::numeric_limits +{ + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; + + // These members were deprecated in C++23 + #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) + static constexpr std::float_denorm_style has_denorm = std::denorm_present; + static constexpr bool has_denorm_loss = true; + #endif + + static constexpr std::float_round_style round_style = std::round_indeterminate; + static constexpr bool is_iec559 = true; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 7; + static constexpr int digits10 = digits; + static constexpr int max_digits10 = digits; + static constexpr int radix = 10; + static constexpr int min_exponent = -95; + static constexpr int min_exponent10 = min_exponent; + static constexpr int max_exponent = 96; + static constexpr int max_exponent10 = max_exponent; + static constexpr bool traps = numeric_limits::traps; + static constexpr bool tinyness_before = true; + + // Member functions + static inline auto (min) () -> boost::decimal::gcc_decimal32 { return {1, min_exponent}; } + static inline auto (max) () -> boost::decimal::gcc_decimal32 { return {9'999'999, max_exponent - digits + 1}; } + static inline auto lowest () -> boost::decimal::gcc_decimal32 { return {-9'999'999, max_exponent - digits + 1}; } + static inline auto epsilon () -> boost::decimal::gcc_decimal32 { return {1, -digits + 1}; } + static inline auto round_error () -> boost::decimal::gcc_decimal32 { return epsilon(); } + + #if defined(__GNUC__) && __GNUC__ >= 8 + # pragma GCC diagnostic push + # pragma GCC diagnostic ignored "-Wclass-memaccess" + #endif + + static inline auto infinity () -> boost::decimal::gcc_decimal32 + { + static_assert(sizeof(std::decimal::decimal32) == sizeof(std::uint32_t), "Decimal32 is an unexpected size."); + constexpr std::uint32_t bits_ = boost::decimal::detail::gccd32_inf_mask; + std::decimal::decimal32 val {}; + std::memcpy(&val, &bits_, sizeof(std::uint32_t)); + return boost::decimal::gcc_decimal32{val}; + } + + static inline auto quiet_NaN () -> boost::decimal::gcc_decimal32 + { + static_assert(sizeof(std::decimal::decimal32) == sizeof(std::uint32_t), "Decimal32 is an unexpected size."); + constexpr std::uint32_t bits_ = boost::decimal::detail::gccd32_qnan_mask; + std::decimal::decimal32 val {}; + std::memcpy(&val, &bits_, sizeof(std::uint32_t)); + return boost::decimal::gcc_decimal32{val}; + } + + static inline auto signaling_NaN() -> boost::decimal::gcc_decimal32 + { + static_assert(sizeof(std::decimal::decimal32) == sizeof(std::uint32_t), "Decimal32 is an unexpected size."); + constexpr std::uint32_t bits_ = boost::decimal::detail::gccd32_snan_mask; + std::decimal::decimal32 val {}; + std::memcpy(&val, &bits_, sizeof(std::uint32_t)); + return boost::decimal::gcc_decimal32{val}; + } + + #if defined(__GNUC__) && __GNUC__ >= 8 + # pragma GCC diagnostic pop + #endif + + static inline auto denorm_min () -> boost::decimal::gcc_decimal32 { return {1, boost::decimal::detail::etiny}; } +}; + +#else + +#error "libstdc++ header is required to use this functionality" + +#endif // __has_include() + +#endif // BOOST_DECIMAL_GCC_DECIMAL32_HPP diff --git a/test/Jamfile b/test/Jamfile index 12d8d38d..3ab24403 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -191,3 +191,9 @@ compile compile_tests/literals_compile.cpp ; compile compile_tests/numbers_compile.cpp ; compile compile_tests/string_compile.cpp ; compile compile_tests/uint128.cpp ; +compile compile_tests/gcc_decimal32.cpp ; + +# Wrapper types +run test_basic_gcc_decimal32.cpp ; +run random_gccdecimal32_comp.cpp ; +run random_gccdecimal32_math.cpp ; diff --git a/test/compile_tests/gcc_decimal32.cpp b/test/compile_tests/gcc_decimal32.cpp new file mode 100644 index 00000000..1da1f0a1 --- /dev/null +++ b/test/compile_tests/gcc_decimal32.cpp @@ -0,0 +1,23 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#ifdef BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL + +#include + +int main() +{ + return 0; +} + +#else + +int main() +{ + return 0; +} + +#endif diff --git a/test/random_gccdecimal32_comp.cpp b/test/random_gccdecimal32_comp.cpp new file mode 100644 index 00000000..7977f31a --- /dev/null +++ b/test/random_gccdecimal32_comp.cpp @@ -0,0 +1,629 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#if defined(BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL) && defined(BOOST_DECIMAL_ENDIAN_LITTLE_BYTE) + +#include +#include +#include +#include +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(1024U); // Number of trials +#else +static constexpr auto N = static_cast(1024U >> 4U); // Number of trials +#endif + +// NOLINTNEXTLINE : Seed with a constant for repeatability +static std::mt19937_64 rng(42); // NOSONAR : Global rng is not const + +template +void random_LT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST((dec1 < dec2) == (val1 < val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge cases + //BOOST_TEST(gcc_decimal32(dist(rng)) < std::numeric_limits::infinity()); + //BOOST_TEST(!(gcc_decimal32(dist(rng)) < -std::numeric_limits::infinity())); + //BOOST_TEST(!(gcc_decimal32(dist(rng)) < std::numeric_limits::quiet_NaN())); + //BOOST_TEST(!(std::numeric_limits::quiet_NaN() < std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_LT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + if (!BOOST_TEST((dec1 < dec2) == (val1 < val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Reverse order of the operands + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(gcc_decimal32(val1))}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST((dec1 < dec2) == (val1 < val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge Cases + //BOOST_TEST_EQ(gcc_decimal32(1) < T(1), false); + //BOOST_TEST_EQ(gcc_decimal32(10) < T(10), false); + //BOOST_TEST_EQ(T(1) < gcc_decimal32(1), false); + //BOOST_TEST_EQ(T(10) < gcc_decimal32(10), false); + //BOOST_TEST_EQ(std::numeric_limits::infinity() < T(1), false); + //BOOST_TEST_EQ(-std::numeric_limits::infinity() < T(1), true); + //BOOST_TEST_EQ(std::numeric_limits::quiet_NaN() < T(1), false); +} + +template +void random_LE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 <= dec2, val1 <= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + //BOOST_TEST(gcc_decimal32(dist(rng)) <= std::numeric_limits::infinity()); + //BOOST_TEST(!(gcc_decimal32(dist(rng)) <= -std::numeric_limits::infinity())); + //BOOST_TEST(!(gcc_decimal32(dist(rng)) <= std::numeric_limits::quiet_NaN())); + //BOOST_TEST(!(std::numeric_limits::quiet_NaN() <= std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_LE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + if (!BOOST_TEST_EQ(dec1 <= dec2, val1 <= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + //BOOST_TEST(dist(rng) <= std::numeric_limits::infinity()); + //BOOST_TEST(!(dist(rng) <= -std::numeric_limits::infinity())); + //BOOST_TEST(!(dist(rng) <= std::numeric_limits::quiet_NaN())); +} + +template +void random_GT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 > dec2, val1 > val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + //BOOST_TEST(!(gcc_decimal32(dist(rng)) > std::numeric_limits::infinity())); + //BOOST_TEST((gcc_decimal32(dist(rng)) > -std::numeric_limits::infinity())); + //BOOST_TEST(!(gcc_decimal32(dist(rng)) > std::numeric_limits::quiet_NaN())); + //BOOST_TEST(!(std::numeric_limits::quiet_NaN() > std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_GT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + if (!BOOST_TEST_EQ(dec1 > dec2, val1 > val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + //BOOST_TEST(!(dist(rng) > std::numeric_limits::infinity())); + //BOOST_TEST((dist(rng) > -std::numeric_limits::infinity())); + //BOOST_TEST(!(dist(rng) > std::numeric_limits::quiet_NaN())); +} + +template +void random_GE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 >= dec2, val1 >= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + //BOOST_TEST(!(gcc_decimal32(dist(rng)) >= std::numeric_limits::infinity())); + //BOOST_TEST((gcc_decimal32(dist(rng)) >= -std::numeric_limits::infinity())); + //BOOST_TEST(!(gcc_decimal32(dist(rng)) >= std::numeric_limits::quiet_NaN())); + //BOOST_TEST(!(std::numeric_limits::quiet_NaN() >= std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_GE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + if (!BOOST_TEST_EQ(dec1 >= dec2, val1 >= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + //BOOST_TEST(!(dist(rng) >= std::numeric_limits::infinity())); + //BOOST_TEST((dist(rng) >= -std::numeric_limits::infinity())); + //BOOST_TEST(!(dist(rng) >= std::numeric_limits::quiet_NaN())); +} + +template +void spot_test_mixed_ge(T lhs, T rhs) +{ + const gcc_decimal32 val1 {lhs}; + const T val2 {static_cast(gcc_decimal32(rhs))}; + + if (!BOOST_TEST_EQ(val1 >= val2, lhs >= rhs)) + { + // LCOV_EXCL_START + std::cerr << " LHS: " << lhs + << "\nLHS D: " << val1 + << "\n RHS: " << rhs + << "\nRHS D: " << val2 << std::endl; + // LCOV_EXCL_STOP + } +} + +template +void random_EQ(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + //BOOST_TEST(!(std::numeric_limits::quiet_NaN() == std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_EQ(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(gcc_decimal32(val1))}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge Cases + //BOOST_TEST_EQ(gcc_decimal32(1), T(1)); + //BOOST_TEST_EQ(gcc_decimal32(10), T(10)); + //BOOST_TEST_EQ(gcc_decimal32(100), T(100)); + //BOOST_TEST_EQ(gcc_decimal32(1000), T(1000)); + //BOOST_TEST_EQ(gcc_decimal32(10000), T(10000)); + //BOOST_TEST_EQ(gcc_decimal32(100000), T(100000)); + //BOOST_TEST_EQ(std::numeric_limits::quiet_NaN() == T(1), false); + //BOOST_TEST_EQ(std::numeric_limits::infinity() == T(1), false); +} + +template +void random_NE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 != dec2, val1 != val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + //BOOST_TEST((std::numeric_limits::quiet_NaN() != std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_NE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + if (!BOOST_TEST_EQ(dec1 != dec2, val1 != val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } +} + +#ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR +template +void random_SPACESHIP(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + if (!BOOST_TEST((dec1 <=> dec2) == (val1 <=> val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST((gcc_decimal32(dist(rng)) <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); + BOOST_TEST((std::numeric_limits::quiet_NaN() <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); +} + +template +void random_mixed_SPACESHIP(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + if (!BOOST_TEST((dec1 <=> dec2) == (val1 <=> val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + if (!BOOST_TEST((dist(rng) <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered)) + { + // LCOV_EXCL_START + const auto eval {dist(rng) <=> std::numeric_limits::quiet_NaN()}; + if (eval == std::partial_ordering::less) + std::cerr << "Less" << std::endl; + else if (eval == std::partial_ordering::greater) + std::cerr << "Greater" << std::endl; + else if (eval == std::partial_ordering::equivalent) + std::cerr << "Equivalent" << std::endl; + else + std::cerr << "Unordered" << std::endl; + // LCOV_EXCL_STOP + } + + BOOST_TEST((std::numeric_limits::quiet_NaN() <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); +} +#endif + +int main() +{ + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + + spot_test_mixed_ge(UINT64_C(15984034765439402622), UINT64_C(1366685175759710132)); + + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + + #ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + #endif + + const auto pos_zero = boost::decimal::gcc_decimal32{0, 0}; + const auto neg_zero = boost::decimal::gcc_decimal32{-0, 0}; + BOOST_TEST_EQ(pos_zero, neg_zero); + + return boost::report_errors(); +} + +#else + +#include + +int main() +{ + std::cerr << "Tests not run" << std::endl; + return 0; +} + +#endif diff --git a/test/random_gccdecimal32_math.cpp b/test/random_gccdecimal32_math.cpp new file mode 100644 index 00000000..617af65f --- /dev/null +++ b/test/random_gccdecimal32_math.cpp @@ -0,0 +1,640 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#if defined(BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL) && defined(BOOST_DECIMAL_ENDIAN_LITTLE_BYTE) + +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Wfloat-conversion" +# pragma GCC diagnostic ignored "-Wconversion" +#endif + +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(1024U); // Number of trials +#else +static constexpr auto N = static_cast(1024U >> 4U); // Number of trials +#endif + +// NOLINTNEXTLINE : Seed with a constant for repeatability +static std::mt19937_64 rng(42); // NOSONAR : Global rng is not const + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4146) +#endif + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + + +template +void random_addition(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + const gcc_decimal32 res = dec1 + dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 + val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + /* + BOOST_TEST(isinf(std::numeric_limits::infinity() + gcc_decimal32{0,0})); + BOOST_TEST(isinf(gcc_decimal32{0,0} + std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() + gcc_decimal32{0,0})); + BOOST_TEST(isnan(gcc_decimal32{0,0} + std::numeric_limits::quiet_NaN())); + */ +} + +template +void random_mixed_addition(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T trunc_val_2 {static_cast(gcc_decimal32(val2))}; + + const gcc_decimal32 res = dec1 + trunc_val_2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 + val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << trunc_val_2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + /* + BOOST_TEST(isinf(std::numeric_limits::infinity() + dist(rng))); + BOOST_TEST(isinf(dist(rng) + std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() + dist(rng))); + BOOST_TEST(isnan(dist(rng) + std::numeric_limits::quiet_NaN())); + */ +} + +template +void spot_random_mixed_addition(T lhs, T rhs) +{ + const T val1 {lhs}; + const T val2 {rhs}; + + const gcc_decimal32 dec1 {val1}; + const T trunc_val_2 {static_cast(gcc_decimal32(val2))}; + + const gcc_decimal32 res = dec1 + trunc_val_2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 + val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << trunc_val_2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } +} + +template +void random_converted_addition(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + // Convert these to and from to ensure rounding + const T val1 {static_cast(gcc_decimal32(dist(rng)))}; + const T val2 {static_cast(gcc_decimal32(dist(rng)))}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + const gcc_decimal32 res {dec1 + dec2}; + const gcc_decimal32 comp_val {val1 + val2}; + + if (!BOOST_TEST_EQ(res, comp_val)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << comp_val << std::endl; + // LCOV_EXCL_STOP + } + } +} + +template +void random_subtraction(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + const gcc_decimal32 res = dec1 - dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + /* + BOOST_TEST(isinf(std::numeric_limits::infinity() - gcc_decimal32{0,0})); + BOOST_TEST(isinf(gcc_decimal32{0,0} - std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() - gcc_decimal32{0,0})); + BOOST_TEST(isnan(gcc_decimal32{0,0} - std::numeric_limits::quiet_NaN())); + */ +} + +template +void random_mixed_subtraction(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T trunc_val_2 {static_cast(gcc_decimal32(val2))}; + + const gcc_decimal32 res = dec1 - trunc_val_2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << trunc_val_2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T trunc_val_1 {static_cast(gcc_decimal32(val1))}; + const gcc_decimal32 dec2 {val2}; + + const gcc_decimal32 res = trunc_val_1 - dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << trunc_val_1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + /* + BOOST_TEST(isinf(std::numeric_limits::infinity() - dist(rng))); + BOOST_TEST(isinf(dist(rng) - std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() - dist(rng))); + BOOST_TEST(isnan(dist(rng) - std::numeric_limits::quiet_NaN())); + */ +} + +template +void random_multiplication(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + const gcc_decimal32 res {dec1 * dec2}; + const gcc_decimal32 res_int {val1 * val2}; + + if (val1 * val2 == 0) + { + // Integers don't have signed 0 but decimal does + continue; + } + + if (!BOOST_TEST_EQ(res, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 * val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + /* + BOOST_TEST(isinf(std::numeric_limits::infinity() * gcc_decimal32(dist(rng)))); + BOOST_TEST(isinf(gcc_decimal32(dist(rng)) * std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() * gcc_decimal32(dist(rng)))); + BOOST_TEST(isnan(gcc_decimal32(dist(rng)) * std::numeric_limits::quiet_NaN())); + */ +} + +template +void random_mixed_multiplication(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + const gcc_decimal32 res {dec1 * dec2}; + const gcc_decimal32 res_int {val1 * val2}; + + if (val1 * val2 == 0) + { + // Integers don't have signed 0 but decimal does + continue; + } + + if (!BOOST_TEST_EQ(res, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 * val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + /* + BOOST_TEST(isinf(std::numeric_limits::infinity() * dist(rng))); + BOOST_TEST(isinf(dist(rng) * std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() * dist(rng))); + BOOST_TEST(isnan(dist(rng) * std::numeric_limits::quiet_NaN())); + */ +} + +template +void random_division(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const gcc_decimal32 dec2 {val2}; + + const auto res {static_cast(dec1 / dec2)}; + const auto res_int {static_cast(val1) / static_cast(val2)}; + + if (std::isinf(res) && std::isinf(res_int)) + { + } + else if (!BOOST_TEST(std::fabs(res - res_int) < 0.001f)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) << std::endl; + // LCOV_EXCL_STOP + } + } + + /* + BOOST_TEST(isinf(std::numeric_limits::infinity() / gcc_decimal32(dist(rng)))); + BOOST_TEST(!isinf(gcc_decimal32(dist(rng)) / std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() / gcc_decimal32(dist(rng)))); + BOOST_TEST(isnan(gcc_decimal32(dist(rng)) / std::numeric_limits::quiet_NaN())); + BOOST_TEST(isinf(gcc_decimal32(dist(rng)) / gcc_decimal32(0))); + */ +} + +template +void random_mixed_division(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const gcc_decimal32 dec1 {val1}; + const T dec2 {static_cast(gcc_decimal32(val2))}; + + const auto res {static_cast(dec1 / dec2)}; + const auto res_int {static_cast(val1) / static_cast(val2)}; + + if (std::isinf(res) && std::isinf(res_int)) + { + } + else if (!BOOST_TEST(std::fabs(res - res_int) < 0.001f)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(gcc_decimal32(val1))}; + const gcc_decimal32 dec2 {val2}; + + const auto res {static_cast(dec1 / dec2)}; + const auto res_int {static_cast(val1) / static_cast(val2)}; + + if (std::isinf(res) && std::isinf(res_int)) + { + } + else if (!BOOST_TEST(std::fabs(res - res_int) < 0.01)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) << std::endl; + // LCOV_EXCL_STOP + } + } + + /* + // Edge cases + const gcc_decimal32 val1 {dist(rng)}; + const gcc_decimal32 zero {0, 0}; + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() / dist(rng))); + BOOST_TEST(isinf(std::numeric_limits::infinity() / dist(rng))); + BOOST_TEST(isnan(dist(rng) / std::numeric_limits::quiet_NaN())); + BOOST_TEST_EQ(abs(dist(rng) / std::numeric_limits::infinity()), zero); + BOOST_TEST(isinf(gcc_decimal32(dist(rng)) / 0)); + BOOST_TEST(isinf(val1 / zero)); + */ +} + +void force_nonfinite() +{ + // INF + gcc_decimal32 val1 {1'000'000}; + + for (int i {}; i < 1000; ++i) + { + val1 *= val1; + } + + std::uint32_t bits {}; + std::memcpy(&bits, &val1, sizeof(std::uint32_t)); + + BOOST_TEST(isinf(val1)); + + // NAN + val1 = 10; + val1 /= gcc_decimal32{0}; + bits = 0U; + + std::memcpy(&bits, &val1, sizeof(std::uint32_t)); + + BOOST_TEST(!isfinite(val1)); +} + +int main() +{ + // Values that won't exceed the range of the significand + // Only positive values + random_addition(0, 5'000'000); + random_addition(0L, 5'000'000L); + random_addition(0LL, 5'000'000LL); + random_mixed_addition(0, 5'000'000); + random_mixed_addition(0L, 5'000'000L); + random_mixed_addition(0LL, 5'000'000LL); + + // Only two negative values + random_addition(-5'000'000, 0); + random_addition(-5'000'000L, 0L); + random_addition(-5'000'000LL, 0LL); + random_mixed_addition(-5'000'000, 0); + random_mixed_addition(-5'000'000L, 0L); + random_mixed_addition(-5'000'000LL, 0LL); + + // Only positive values + random_subtraction(0, 5'000'000); + random_subtraction(0L, 5'000'000L); + random_subtraction(0LL, 5'000'000LL); + random_mixed_subtraction(0, 5'000'000); + random_mixed_subtraction(0L, 5'000'000L); + random_mixed_subtraction(0LL, 5'000'000LL); + + // Only two negative values + random_subtraction(-5'000'000, 0); + random_subtraction(-5'000'000L, 0L); + random_subtraction(-5'000'000LL, 0LL); + random_mixed_subtraction(-5'000'000, 0); + random_mixed_subtraction(-5'000'000L, 0L); + random_mixed_subtraction(-5'000'000LL, 0LL); + + // Mixed Values + random_subtraction(-5'000'000, 5'000'000); + random_subtraction(-5'000'000L, 5'000'000L); + random_subtraction(-5'000'000LL, 5'000'000LL); + random_mixed_subtraction(-5'000'000, 5'000'000); + random_mixed_subtraction(-5'000'000L, 5'000'000L); + random_mixed_subtraction(-5'000'000LL, 5'000'000LL); + + // Anything in range + random_addition(-5'000'000, 5'000'000); + random_addition(-5'000'000L, 5'000'000L); + random_addition(-5'000'000LL, 5'000'000LL); + random_mixed_addition(-5'000'000, 5'000'000); + random_mixed_addition(-5'000'000L, 5'000'000L); + random_mixed_addition(-5'000'000LL, 5'000'000LL); + + // Anything in the domain + random_converted_addition(0, (std::numeric_limits::max)() / 2); + random_converted_addition((std::numeric_limits::min)() / 2, 0); + random_converted_addition((std::numeric_limits::min)() / 2, (std::numeric_limits::max)() / 2); + + // Positive values + const auto sqrt_int_max = static_cast(std::sqrt(static_cast((std::numeric_limits::max)()))); + + random_multiplication(0, 5'000); + random_multiplication(0L, 5'000L); + random_multiplication(0LL, 5'000LL); + random_multiplication(0, sqrt_int_max); + random_mixed_multiplication(0, 5'000); + random_mixed_multiplication(0L, 5'000L); + random_mixed_multiplication(0LL, 5'000LL); + random_mixed_multiplication(0, sqrt_int_max); + + // Only negative values + random_multiplication(-5'000, 0); + random_multiplication(-5'000L, 0L); + random_multiplication(-5'000LL, 0LL); + random_multiplication(-sqrt_int_max, 0); + random_mixed_multiplication(-5'000, 0); + random_mixed_multiplication(-5'000L, 0L); + random_mixed_multiplication(-5'000LL, 0LL); + random_mixed_multiplication(-sqrt_int_max, 0); + + // Mixed values + random_multiplication(-5'000, 5'000); + random_multiplication(-5'000L, 5'000L); + random_multiplication(-5'000LL, 5'000LL); + random_multiplication(-sqrt_int_max, sqrt_int_max); + random_mixed_multiplication(-5'000, 5'000); + random_mixed_multiplication(-5'000L, 5'000L); + random_mixed_multiplication(-5'000LL, 5'000LL); + random_mixed_multiplication(-sqrt_int_max, sqrt_int_max); + + random_division(0, 5'000); + random_division(0L, 5'000L); + random_division(0LL, 5'000LL); + random_division(0, sqrt_int_max); + random_mixed_division(0, 5'000); + random_mixed_division(0L, 5'000L); + random_mixed_division(0LL, 5'000LL); + random_mixed_division(0, sqrt_int_max); + + // Only negative values + random_division(-5'000, 0); + random_division(-5'000L, 0L); + random_division(-5'000LL, 0LL); + random_division(-sqrt_int_max, 0); + random_mixed_division(-5'000, 0); + random_mixed_division(-5'000L, 0L); + random_mixed_division(-5'000LL, 0LL); + random_mixed_division(-sqrt_int_max, 0); + + // Mixed values + random_division(-5'000, 5'000); + random_division(-5'000L, 5'000L); + random_division(-5'000LL, 5'000LL); + random_division(-sqrt_int_max, sqrt_int_max); + random_mixed_division(-5'000, 5'000); + random_mixed_division(-5'000L, 5'000L); + random_mixed_division(-5'000LL, 5'000LL); + random_mixed_division(-sqrt_int_max, sqrt_int_max); + + spot_random_mixed_addition(-653573LL, 1391401LL); + spot_random_mixed_addition(894090LL, -1886315LL); + + force_nonfinite(); + + return boost::report_errors(); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic pop +#endif + +#else + +int main() +{ + return 0; +} + +#endif diff --git a/test/test_basic_gcc_decimal32.cpp b/test/test_basic_gcc_decimal32.cpp new file mode 100644 index 00000000..fe86cec0 --- /dev/null +++ b/test/test_basic_gcc_decimal32.cpp @@ -0,0 +1,109 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#define BOOST_DECIMAL_DEBUG_ACCESSORS + +#include + +#if defined(BOOST_DECIMAL_HAS_LIBSTDCPP_DECIMAL) && defined(BOOST_DECIMAL_ENDIAN_LITTLE_BYTE) + +#include +#include +#include +#include +#include +#include +#include +#include + +void compare_bits(long long coeff, int exp) +{ + const boost::decimal::decimal32 dec32_val {coeff, exp}; + const boost::decimal::gcc_decimal32 gcc_val {coeff, exp}; + + if (!BOOST_TEST_EQ(dec32_val.full_significand(), gcc_val.full_significand()) || + !BOOST_TEST_EQ(dec32_val.biased_exponent(), gcc_val.biased_exponent()) || + !BOOST_TEST_EQ(dec32_val.unbiased_exponent(), gcc_val.unbiased_exponent()) || + !BOOST_TEST_EQ(dec32_val.isneg(), gcc_val.isneg())) + { + // LCOV_EXCL_START + std::uint32_t boost_bits; + std::memcpy(&boost_bits, &dec32_val, sizeof(std::uint32_t)); + + std::uint32_t gcc_bits; + std::memcpy(&gcc_bits, &gcc_val, sizeof(std::uint32_t)); + + std::cerr << "Coeff: " << coeff << '\n' + << " Exp: " << exp << '\n' + << "Boost: " << std::bitset<32>(boost_bits) << "\n" + << " GCC: " << std::bitset<32>(gcc_bits) << "\n" << std::endl; + // LCOV_EXCL_STOP + } + + // Separate test of the to_components + const auto dec_struct = dec32_val.to_components(); + const auto gcc_struct = gcc_val.to_components(); + + BOOST_TEST_EQ(gcc_struct.sign, gcc_val.isneg()); + BOOST_TEST_EQ(gcc_struct.sig, gcc_val.full_significand()); + BOOST_TEST_EQ(gcc_struct.exp, gcc_val.biased_exponent()); + + BOOST_TEST_EQ(dec_struct.sign, gcc_struct.sign); + BOOST_TEST_EQ(dec_struct.sig, gcc_struct.sig); + BOOST_TEST_EQ(dec_struct.exp, gcc_struct.exp); +} + +int main() +{ + // 2^0 + compare_bits(1, 0); + + // 2^1 + compare_bits(2, 0); + + // 2^21 + compare_bits(2097152, 0); + + // 2^22 + compare_bits(4194304, 0); + + // 2^23 + compare_bits(8388608, 0); + + // 2^24 + // Exceeds digits10 so needs to be rounded + compare_bits(16777216, 0); + + // Random significands with 0 exponent + std::mt19937_64 rng{42}; + std::uniform_int_distribution sig_dist(-10000000, 1000000); + for (int i {}; i < 1024; ++i) + { + compare_bits(sig_dist(rng), 0); + } + + // Random powers of 2 + std::uniform_int_distribution exp_dist(-88, 89); + for (int i {}; i < 1024; ++i) + { + compare_bits(2, exp_dist(rng)); + } + + // Put it all together now + for (int i {}; i < 1024; ++i) + { + compare_bits(sig_dist(rng), exp_dist(rng)); + } + + return boost::report_errors(); +} + +#else + +int main() +{ + return 0; +} + +#endif