diff --git a/include/boost/decimal/detail/cmath/ceil.hpp b/include/boost/decimal/detail/cmath/ceil.hpp index 219d129c4..abaf05731 100644 --- a/include/boost/decimal/detail/cmath/ceil.hpp +++ b/include/boost/decimal/detail/cmath/ceil.hpp @@ -6,14 +6,10 @@ #define BOOST_DECIMAL_DETAIL_CMATH_CEIL_HPP #include -#include #include -#include -#include #include +#include #include -#include -#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -27,63 +23,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto ceil BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (const T val) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - using DivType = typename T::significand_type; - constexpr int inverse_eps {std::is_same::value ? 7 : - std::is_same::value ? 16 : 34}; - - constexpr T zero {0, 0}; - constexpr T one {1, 0}; - constexpr T max_comp_value {1u, inverse_eps}; - const auto fp {fpclassify(val)}; - - switch (fp) - { - case FP_ZERO: - case FP_NAN: - case FP_INFINITE: - return val; - default: - static_cast(val); - } - - if (val > zero && val >= max_comp_value) - { - return val; - } - - int exp_ptr {}; - auto new_sig {frexp10(val, &exp_ptr)}; - const auto abs_exp {detail::make_positive_unsigned(exp_ptr)}; - const bool is_neg {val < zero}; - - const auto sig_dig {detail::precision_v}; - auto decimal_digits {static_cast(sig_dig)}; - const auto zero_digits {detail::remove_trailing_zeros(new_sig).number_of_removed_zeros}; - const auto non_zero_exp {exp_ptr + static_cast(zero_digits)}; - - if (non_zero_exp >= 0) - { - // If the value is an integer, nothing should occur - return val; - } - - if (sig_dig > abs_exp) - { - decimal_digits = abs_exp; - } - else if (exp_ptr < 1 && abs_exp >= sig_dig) - { - return is_neg ? zero : one; - } - - new_sig /= detail::pow10(decimal_digits); - if (!is_neg) - { - ++new_sig; - } - new_sig *= 10U; - - return T{new_sig, exp_ptr + static_cast(decimal_digits) - 1, is_neg}; + return detail::round_integral_impl(val, rounding_mode::fe_dec_upward); } } // namespace decimal diff --git a/include/boost/decimal/detail/cmath/floor.hpp b/include/boost/decimal/detail/cmath/floor.hpp index 94567c2aa..0a3e836ff 100644 --- a/include/boost/decimal/detail/cmath/floor.hpp +++ b/include/boost/decimal/detail/cmath/floor.hpp @@ -8,13 +8,8 @@ #include #include #include -#include -#include -#include -#include -#include "../int128.hpp" #include -#include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -28,54 +23,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto floor BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (const T val) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - using DivType = typename T::significand_type; - - constexpr T zero {0, 0}; - constexpr T neg_one {1U, 0, construction_sign::negative}; - const auto fp {fpclassify(val)}; - - switch (fp) - { - case FP_ZERO: - case FP_NAN: - case FP_INFINITE: - return val; - default: - static_cast(val); - } - - int exp_ptr {}; - auto new_sig {frexp10(val, &exp_ptr)}; - const auto abs_exp {detail::make_positive_unsigned(exp_ptr)}; - const bool is_neg {val < zero}; - - const auto sig_dig {detail::precision_v}; - auto decimal_digits {static_cast(sig_dig)}; - const auto zero_digits {detail::remove_trailing_zeros(new_sig).number_of_removed_zeros}; - const auto non_zero_exp {exp_ptr + static_cast(zero_digits)}; - - if (non_zero_exp >= 0) - { - // If the value is an integer, nothing should occur - return val; - } - - if (sig_dig > abs_exp) - { - decimal_digits = abs_exp; - } - else if (exp_ptr < 1 && abs_exp >= sig_dig) - { - return is_neg ? neg_one : zero; - } - - new_sig /= detail::pow10(decimal_digits); - if (is_neg) - { - ++new_sig; - } - - return T{new_sig, exp_ptr + static_cast(decimal_digits), is_neg}; + return detail::round_integral_impl(val, rounding_mode::fe_dec_downward); } } // namespace decimal diff --git a/include/boost/decimal/detail/cmath/impl/round_integral.hpp b/include/boost/decimal/detail/cmath/impl/round_integral.hpp new file mode 100644 index 000000000..953df5bdf --- /dev/null +++ b/include/boost/decimal/detail/cmath/impl/round_integral.hpp @@ -0,0 +1,100 @@ +// Copyright 2026 Shen-Ta Hsieh +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_CMATH_IMPL_ROUND_INTEGRAL_HPP +#define BOOST_DECIMAL_DETAIL_CMATH_IMPL_ROUND_INTEGRAL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace decimal { +namespace detail { + +// Rounds c.sig * 10^c.exp, with c.exp < 0, to an integer in the mode round: +// one division by 10^-c.exp +template +BOOST_DECIMAL_FORCE_INLINE constexpr auto round_integral_sig(const Components& c, const rounding_mode round) noexcept -> typename T::significand_type +{ + using sig_type = typename T::significand_type; + + const auto shift {-static_cast(c.exp)}; + sig_type q {0U}; + bool inc {}; + + if (shift > precision_v) + { + // 0 < |x| < 0.1 + inc = round == (c.sign ? rounding_mode::fe_dec_downward : rounding_mode::fe_dec_upward); + } + else + { + const auto p {pow10(static_cast(shift))}; + q = static_cast(c.sig / p); + const auto r {static_cast(c.sig - q * p)}; + if (r == 0U) + { + return q; + } + + switch (round) + { + case rounding_mode::fe_dec_upward: + inc = !c.sign; + break; + case rounding_mode::fe_dec_downward: + inc = c.sign; + break; + case rounding_mode::fe_dec_to_nearest_from_zero: + inc = r >= p - r; + break; + case rounding_mode::fe_dec_to_nearest: + inc = r > p - r || (r == p - r && (static_cast(q) & 1U) == 1U); + break; + case rounding_mode::fe_dec_toward_zero: + default: + break; + } + } + + if (inc) + { + ++q; + } + + return q; +} + +// Rounds x to an integer in the mode round. The result has exponent 0, and a zero result +// keeps the sign of x. +template +constexpr auto round_integral_impl(const T x, const rounding_mode round) noexcept -> T +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (!isfinite(x)) + { + return x; + } + #endif + + const auto c {decompose(x)}; + if (c.exp >= 0 || c.sig == 0U) + { + return x; + } + + return pack_in_range(round_integral_sig(c, round), 0, c.sign); +} + +} // namespace detail +} // namespace decimal +} // namespace boost + +#endif // BOOST_DECIMAL_DETAIL_CMATH_IMPL_ROUND_INTEGRAL_HPP diff --git a/include/boost/decimal/detail/cmath/rint.hpp b/include/boost/decimal/detail/cmath/rint.hpp index 5f299b762..7fa418510 100644 --- a/include/boost/decimal/detail/cmath/rint.hpp +++ b/include/boost/decimal/detail/cmath/rint.hpp @@ -8,13 +8,8 @@ #include #include #include -#include -#include +#include #include -#include -#include -#include -#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -28,15 +23,26 @@ namespace decimal { namespace detail { -template -constexpr auto rint_impl(T1& sig, T2 exp, const bool is_neg) +// The current rounding mode +template +constexpr auto current_rounding_mode(const T x) noexcept -> rounding_mode { - const T2 abs_exp { (exp < T2(0)) ? -exp : exp }; + static_cast(x); + auto round {_boost_decimal_global_rounding_mode}; + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + if (!BOOST_DECIMAL_IS_CONSTANT_EVALUATED(x)) + { + round = _boost_decimal_global_runtime_rounding_mode; + } + #endif - const auto res {detail::impl::divmod(sig, detail::pow10(static_cast(abs_exp - 1)))}; - sig = res.quotient; + return round; +} - detail::fenv_round(sig, is_neg, res.remainder != 0U); +template +constexpr auto lrint_saturate(const bool sign) noexcept -> Int +{ + return sign ? (std::numeric_limits::min)() : (std::numeric_limits::max)(); } // MSVC 14.1 warns of unary minus being applied to unsigned type from numeric_limits::min @@ -49,58 +55,60 @@ constexpr auto rint_impl(T1& sig, T2 exp, const bool is_neg) template constexpr auto lrint_impl(const T num) noexcept -> Int { - constexpr T zero {0, 0}; - constexpr T lmax {(std::numeric_limits::max)()}; - constexpr T lmin {(std::numeric_limits::min)()}; + using sig_type = typename T::significand_type; + using uint_type = std::make_unsigned_t; + using work_type = std::conditional_t<(sizeof(sig_type) > sizeof(uint_type)), sig_type, uint_type>; #ifndef BOOST_DECIMAL_FAST_MATH - if (isinf(num) || isnan(num)) + if (!isfinite(num)) { // Implementation defined what to return here return std::numeric_limits::min(); } - if (abs(num) == zero) - { - return 0; - } - #else - if (abs(num) == zero) - { - return zero; - } #endif - int expptr {}; - auto sig {frexp10(num, &expptr)}; // Always returns detail::precision digits - const bool is_neg {num < 0}; - - if (num > lmax) + const auto c {decompose(num)}; + if (c.sig == 0U) { - return (std::numeric_limits::max)(); - } - else if (num < lmin) - { - return (std::numeric_limits::min)(); + return 0; } - if (expptr >= 0) + // Largest magnitude of the result: max for x > 0, and -min for x < 0 + const auto limit {static_cast(static_cast((std::numeric_limits::max)()) + (c.sign ? 1U : 0U))}; + work_type mag {}; + if (c.exp >= 0) { - return static_cast(num); + // 10^exp overflows work_type: saturate + if (c.exp > std::numeric_limits::digits10) + { + return lrint_saturate(c.sign); + } + const auto p {pow10(static_cast(c.exp))}; + if (static_cast(c.sig) > limit / p) + { + return lrint_saturate(c.sign); + } + mag = static_cast(static_cast(c.sig) * p); } - else if (expptr < -detail::precision_v) + else { - return 0; + // |x| >= 10^(digits10 + 1) is out of range: saturate before the division + constexpr int int_digits {std::numeric_limits::digits10 + 1}; + const auto shift {-static_cast(c.exp)}; + if (shift < precision_v - int_digits && c.sig >= pow10(static_cast(shift + int_digits))) + { + return lrint_saturate(c.sign); + } + mag = static_cast(round_integral_sig(c, current_rounding_mode(num))); } - detail::rint_impl(sig, expptr, is_neg); - - auto res {static_cast(sig)}; - if (is_neg) + if (mag > limit) { - res = -res; + return lrint_saturate(c.sign); } - return res; + const auto umag {static_cast(mag)}; + return static_cast(c.sign ? static_cast(0U - umag) : umag); } #ifdef _MSC_VER @@ -114,37 +122,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto rint(const T num) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T zero {0, 0}; - constexpr T max_round_value {1 / std::numeric_limits::epsilon()}; - - #ifndef BOOST_DECIMAL_FAST_MATH - if (isinf(num) || isnan(num) || abs(num) == zero || abs(num) > max_round_value) - { - return num; - } - #else - if (abs(num) == zero || abs(num) > max_round_value) - { - return num; - } - #endif - - int expptr {}; - auto sig {frexp10(num, &expptr)}; // Always returns detail::precision digits - const bool is_neg {num < 0}; - - if (expptr >= 0) - { - return num; - } - else if (expptr < -detail::precision_v) - { - return is_neg ? -zero : zero; - } - - detail::rint_impl(sig, expptr, is_neg); - - return {sig, 0, is_neg}; + return detail::round_integral_impl(num, detail::current_rounding_mode(num)); } BOOST_DECIMAL_EXPORT template diff --git a/include/boost/decimal/detail/cmath/round.hpp b/include/boost/decimal/detail/cmath/round.hpp index bbb478306..7743d0d1c 100644 --- a/include/boost/decimal/detail/cmath/round.hpp +++ b/include/boost/decimal/detail/cmath/round.hpp @@ -8,8 +8,8 @@ #include #include #include +#include #include -#include #include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -25,34 +25,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto round(const T num) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - constexpr T zero {0, 0}; - constexpr T half {5, -1}; - - #ifndef BOOST_DECIMAL_FAST_MATH - if (isnan(num) || isinf(num) || abs(num) == zero) - { - return num; - } - #else - if (abs(num) == zero) - { - return num; - } - #endif - - T iptr {}; - const auto x {modf(num, &iptr)}; - - if (x >= half) - { - ++iptr; - } - else if (x <= -half) - { - --iptr; - } - - return iptr; + return detail::round_integral_impl(num, rounding_mode::fe_dec_to_nearest_from_zero); } namespace detail { diff --git a/include/boost/decimal/detail/cmath/roundeven.hpp b/include/boost/decimal/detail/cmath/roundeven.hpp index 4f1e8b443..fc58f19c6 100644 --- a/include/boost/decimal/detail/cmath/roundeven.hpp +++ b/include/boost/decimal/detail/cmath/roundeven.hpp @@ -8,13 +8,8 @@ #include #include #include -#include -#include -#include -#include -#include "../int128.hpp" #include -#include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -28,66 +23,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto roundeven BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (const T val) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - using DivType = typename T::significand_type; - - constexpr T zero {0, 0}; - constexpr T neg_zero {0U, 0, construction_sign::negative}; - constexpr T one {1U, 0}; - constexpr T neg_one {1U, 0, construction_sign::negative}; - const auto fp {fpclassify(val)}; - - switch (fp) - { - case FP_ZERO: - case FP_NAN: - case FP_INFINITE: - return val; - default: - static_cast(val); - } - - int exp_ptr {}; - auto new_sig {frexp10(val, &exp_ptr)}; - const auto abs_exp {detail::make_positive_unsigned(exp_ptr)}; - const bool is_neg {val < zero}; - - const auto sig_dig {detail::precision_v}; - auto decimal_digits {static_cast(sig_dig)}; - const auto zero_digits {detail::remove_trailing_zeros(new_sig).number_of_removed_zeros}; - const auto non_zero_exp {exp_ptr + static_cast(zero_digits)}; - - if (non_zero_exp >= 0) - { - // If the value is an integer, nothing should occur - return val; - } - - if (sig_dig > abs_exp) - { - decimal_digits = abs_exp; - } - else if (exp_ptr < 1 && abs_exp >= sig_dig) - { - if (abs_exp == sig_dig && new_sig > detail::pow10(decimal_digits) / 2U) - { - return is_neg ? neg_one : one; - } - else - { - return is_neg ? neg_zero : zero; - } - } - - const auto pow_ten {detail::pow10(decimal_digits)}; - const auto trailing_digits {new_sig % pow_ten}; - const auto half_power_ten {pow_ten / 2U}; - new_sig /= pow_ten; - if (trailing_digits > half_power_ten || (trailing_digits == half_power_ten && (static_cast(new_sig) & 1U) == 1U)) - { - ++new_sig; - } - - return T{new_sig, exp_ptr + static_cast(decimal_digits), is_neg}; + return detail::round_integral_impl(val, rounding_mode::fe_dec_to_nearest); } } // namespace decimal diff --git a/include/boost/decimal/detail/cmath/trunc.hpp b/include/boost/decimal/detail/cmath/trunc.hpp index e72a16d6a..3f2cf91a2 100644 --- a/include/boost/decimal/detail/cmath/trunc.hpp +++ b/include/boost/decimal/detail/cmath/trunc.hpp @@ -9,10 +9,7 @@ #include #include #include -#include -#include -#include -#include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -26,7 +23,7 @@ BOOST_DECIMAL_EXPORT template constexpr auto trunc(const T val) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - return (val > 0) ? floor(val) : ceil(val); + return detail::round_integral_impl(val, rounding_mode::fe_dec_toward_zero); } } // namespace decimal diff --git a/test/Jamfile b/test/Jamfile index 9de4c0143..8f8e2087c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -144,6 +144,7 @@ run github_issue_1467.cpp ; run github_issue_1473.cpp ; run github_issue_1476.cpp ; run github_issue_1482.cpp ; +run github_issue_1485.cpp ; run link_1.cpp link_2.cpp link_3.cpp ; run quick.cpp ; diff --git a/test/github_issue_1485.cpp b/test/github_issue_1485.cpp new file mode 100644 index 000000000..d8783dc72 --- /dev/null +++ b/test/github_issue_1485.cpp @@ -0,0 +1,168 @@ +// Copyright 2026 Shen-Ta Hsieh +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// See: https://github.com/boostorg/decimal/issues/1485 + +#include +#include +#include + +using namespace boost::decimal; +using namespace boost::decimal::literals; + +template +bool is_pos_zero(const T x) +{ + return x == 0 && !signbit(x); +} + +template +bool is_neg_zero(const T x) +{ + return x == 0 && signbit(x); +} + +// A zero result keeps the sign of x, also when |x| is below the last digit of the type, +// and modf, which uses ceil and floor, keeps it too +template +void test_signed_zero() +{ + constexpr int tiny {-std::numeric_limits::digits10 - 5}; + + BOOST_TEST(is_neg_zero(ceil(T(5U, -1, true)))); + BOOST_TEST(is_neg_zero(ceil(T(9U, -1, true)))); + BOOST_TEST(is_neg_zero(ceil(T(1U, tiny, true)))); + BOOST_TEST(is_pos_zero(floor(T(5U, -1)))); + BOOST_TEST(is_pos_zero(floor(T(1U, tiny)))); + BOOST_TEST(is_neg_zero(round(T(4U, -1, true)))); + BOOST_TEST(is_neg_zero(round(T(1U, tiny, true)))); + BOOST_TEST(is_pos_zero(round(T(4U, -1)))); + BOOST_TEST(is_neg_zero(roundeven(T(5U, -1, true)))); + BOOST_TEST(is_neg_zero(roundeven(T(1U, tiny, true)))); + BOOST_TEST(is_pos_zero(roundeven(T(5U, -1)))); + BOOST_TEST(is_neg_zero(trunc(T(5U, -1, true)))); + BOOST_TEST(is_pos_zero(trunc(T(5U, -1)))); + + T ip {}; + static_cast(modf(T(4U, -1, true), &ip)); + BOOST_TEST(is_neg_zero(ip)); + + // A value below the last digit and away from zero still goes to one + BOOST_TEST_EQ(ceil(T(1U, tiny)), T(1)); + BOOST_TEST_EQ(floor(T(1U, tiny, true)), T(-1)); +} + +// rint, nearbyint, lrint and llrint round in the current mode, also when |x| is below 0.1 +template +void test_rint() +{ + constexpr int tiny {-std::numeric_limits::digits10 - 5}; + + BOOST_TEST_EQ(rint(T(5U, -1)), T(0)); + BOOST_TEST_EQ(rint(T(15U, -1)), T(2)); + BOOST_TEST(is_neg_zero(rint(T(4U, -1, true)))); + BOOST_TEST(is_neg_zero(nearbyint(T(1U, tiny, true)))); + + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + // fesetround has an effect only with the detection of constant evaluation + fesetround(rounding_mode::fe_dec_upward); + BOOST_TEST_EQ(rint(T(1U, tiny)), T(1)); + BOOST_TEST_EQ(rint(T(4U, -2)), T(1)); + BOOST_TEST_EQ(nearbyint(T(11U, -1)), T(2)); + BOOST_TEST(is_neg_zero(rint(T(1U, tiny, true)))); + BOOST_TEST_EQ(lrint(T(1U, tiny)), 1L); + BOOST_TEST_EQ(llrint(T(4U, -2)), 1LL); + + fesetround(rounding_mode::fe_dec_downward); + BOOST_TEST_EQ(rint(T(1U, tiny, true)), T(-1)); + BOOST_TEST_EQ(rint(T(4U, -2, true)), T(-1)); + BOOST_TEST_EQ(nearbyint(T(19U, -1)), T(1)); + BOOST_TEST(is_pos_zero(rint(T(1U, tiny)))); + BOOST_TEST_EQ(lrint(T(1U, tiny, true)), -1L); + BOOST_TEST_EQ(llrint(T(4U, -2, true)), -1LL); + + fesetround(rounding_mode::fe_dec_toward_zero); + BOOST_TEST(is_neg_zero(rint(T(9U, -1, true)))); + BOOST_TEST_EQ(rint(T(19U, -1, true)), T(-1)); + + fesetround(rounding_mode::fe_dec_to_nearest_from_zero); + BOOST_TEST_EQ(rint(T(5U, -1, true)), T(-1)); + BOOST_TEST_EQ(rint(T(25U, -1)), T(3)); + BOOST_TEST(is_pos_zero(rint(T(4U, -2)))); + + fesetround(rounding_mode::fe_dec_to_nearest); + #endif +} + +// llrint saturates out of range and does not wrap to the other sign +void test_llrint_range() +{ + BOOST_TEST_EQ(llrint("1e20"_DD), LLONG_MAX); + BOOST_TEST_EQ(llrint("-1e20"_DD), LLONG_MIN); + BOOST_TEST_EQ(llrint("9.223372036854776e18"_DD), LLONG_MAX); + BOOST_TEST_EQ(llrint("-9.223372036854776e18"_DD), LLONG_MIN); + BOOST_TEST_EQ(llrint("9.223372036854776e18"_DDF), LLONG_MAX); + BOOST_TEST_EQ(llrint("-9.223372036854776e18"_DDF), LLONG_MIN); + BOOST_TEST_EQ(llrint("9223372036854775808"_DL), LLONG_MAX); + BOOST_TEST_EQ(llrint("-9223372036854775808"_DL), LLONG_MIN); + BOOST_TEST_EQ(llrint("9223372036854775807.5"_DL), LLONG_MAX); + BOOST_TEST_EQ(llrint("-9223372036854775808.4"_DL), LLONG_MIN); + BOOST_TEST_EQ(llrint("-9223372036854775808.6"_DL), LLONG_MIN); + BOOST_TEST_EQ(llrint("100000000000000000000.5"_DL), LLONG_MAX); + BOOST_TEST_EQ(llrint("9223372036854775806.5"_DL), 9223372036854775806LL); +} + +// IEEE 754 gives the result the exponent max(q(x), 0) for the types with cohorts +void test_quantum() +{ + BOOST_TEST_EQ(quantexp(ceil("1.5"_DF)), 0); + BOOST_TEST_EQ(quantexp(ceil("-1.5"_DF)), 0); + BOOST_TEST_EQ(quantexp(floor("-1.5"_DF)), 0); + BOOST_TEST_EQ(quantexp(round("2.5"_DF)), 0); + BOOST_TEST_EQ(quantexp(roundeven("2.5"_DF)), 0); + BOOST_TEST_EQ(quantexp(ceil("2.0"_DF)), 0); + BOOST_TEST_EQ(quantexp(ceil("2e3"_DF)), 3); + BOOST_TEST_EQ(ceil("999999.9"_DF), "1000000"_DF); + BOOST_TEST_EQ(quantexp(ceil("999999.9"_DF)), 0); + + BOOST_TEST_EQ(quantexp(ceil("1.25"_DD)), 0); + BOOST_TEST_EQ(quantexp(floor("-1.25"_DD)), 0); + BOOST_TEST_EQ(quantexp(round("-2.50"_DD)), 0); + BOOST_TEST_EQ(quantexp(roundeven("3.50"_DD)), 0); + BOOST_TEST_EQ(quantexp(floor("7.000"_DD)), 0); + BOOST_TEST_EQ(ceil("999999999999999.9"_DD), "1000000000000000"_DD); + BOOST_TEST_EQ(quantexp(ceil("999999999999999.9"_DD)), 0); + + BOOST_TEST_EQ(quantexp(ceil("1.25"_DL)), 0); + BOOST_TEST_EQ(quantexp(floor("-1.25"_DL)), 0); + BOOST_TEST_EQ(quantexp(round("12345678901234567890.5"_DL)), 0); + BOOST_TEST_EQ(quantexp(roundeven("-0.5"_DL)), 0); + BOOST_TEST_EQ(quantexp(round("5e2"_DL)), 2); + BOOST_TEST_EQ(round("12345678901234567890.5"_DL), "12345678901234567891"_DL); + BOOST_TEST_EQ(quantexp(rint("2.0"_DF)), 0); + BOOST_TEST_EQ(quantexp(nearbyint("-2.50"_DD)), 0); + BOOST_TEST_EQ(quantexp(trunc("-2.5"_DL)), 0); +} + +int main() +{ + test_signed_zero(); + test_signed_zero(); + test_signed_zero(); + test_signed_zero(); + test_signed_zero(); + test_signed_zero(); + + test_rint(); + test_rint(); + test_rint(); + test_rint(); + test_rint(); + test_rint(); + + test_llrint_range(); + test_quantum(); + + return boost::report_errors(); +} diff --git a/test/test_cmath.cpp b/test/test_cmath.cpp index 306fb382e..343121927 100644 --- a/test/test_cmath.cpp +++ b/test/test_cmath.cpp @@ -187,7 +187,7 @@ void test_floor() BOOST_TEST(isinf(floor(std::numeric_limits::infinity() * dist(rng)))); BOOST_TEST(isinf(floor(-std::numeric_limits::infinity() * dist(rng)))); BOOST_TEST_EQ(floor(Dec(0, 0) * dist(rng)), Dec(0, 0)); - BOOST_TEST_EQ(floor(Dec(-0, 0) * dist(rng)), Dec(-0, 0)); + BOOST_TEST(signbit(floor(Dec(0U, 0, true) * dist(rng)))); BOOST_TEST_EQ(floor(Dec(27, -1) * dist(rng)), Dec(2, 0)); BOOST_TEST_EQ(floor(Dec(-27, -1) * dist(rng)), Dec(-3, 0)); @@ -216,7 +216,7 @@ void test_ceil() BOOST_TEST(isinf(ceil(std::numeric_limits::infinity() * dist(rng)))); BOOST_TEST(isinf(ceil(-std::numeric_limits::infinity() * dist(rng)))); BOOST_TEST_EQ(ceil(Dec(0, 0) * dist(rng)), Dec(0, 0)); - BOOST_TEST_EQ(ceil(Dec(-0, 0) * dist(rng)), Dec(-0, 0)); + BOOST_TEST(signbit(ceil(Dec(0U, 0, true) * dist(rng)))); BOOST_TEST_EQ(ceil(Dec(27, -1)), Dec(3, 0)); BOOST_TEST_EQ(ceil(Dec(-27, -1)), Dec(-2, 0)); @@ -243,7 +243,7 @@ void test_trunc() BOOST_TEST(isinf(trunc(std::numeric_limits::infinity()))); BOOST_TEST(isinf(trunc(-std::numeric_limits::infinity()))); BOOST_TEST_EQ(trunc(Dec(0, 0)), Dec(0, 0)); - BOOST_TEST_EQ(trunc(Dec(-0, 0)), Dec(-0, 0)); + BOOST_TEST(signbit(trunc(Dec(0U, 0, true)))); BOOST_TEST_EQ(trunc(Dec(27, -1)), Dec(2, 0)); BOOST_TEST_EQ(trunc(Dec(-27, -1)), Dec(-2, 0)); @@ -261,7 +261,7 @@ void test_roundeven() BOOST_TEST(isinf(roundeven(std::numeric_limits::infinity() * dist(rng)))); BOOST_TEST(isinf(roundeven(-std::numeric_limits::infinity() * dist(rng)))); BOOST_TEST_EQ(roundeven(Dec(0, 0) * dist(rng)), Dec(0, 0)); - BOOST_TEST_EQ(roundeven(Dec(-0, 0) * dist(rng)), Dec(-0, 0)); + BOOST_TEST(signbit(roundeven(Dec(0U, 0, true) * dist(rng)))); BOOST_TEST_EQ(roundeven(Dec(5, -1) * dist(rng)), Dec(0, 0)); BOOST_TEST_EQ(roundeven(Dec(15, -1) * dist(rng)), Dec(2, 0));