// Copyright John Maddock 2007. // Copyright Matt Borland 2023. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include template typename std::enable_if::value>::type check_within_half(T a, U u) { BOOST_MATH_STD_USING const auto fabs_res {fabs(a - u)}; if (fabs_res > 0.5f) { // LCOV_EXCL_START BOOST_ERROR("Rounded result differed by more than 0.5 from the original"); std::cerr << "Values were: " << std::setprecision(35) << std::setw(40) << std::left << a << u << std::endl; // LCOV_EXCL_STOP } if ((fabs(a - u) == 0.5f) && (fabs(static_cast(u)) < fabs(a))) { // LCOV_EXCL_START BOOST_ERROR("Rounded result was towards zero with boost::round"); std::cerr << "Values were: " << std::setprecision(35) << std::setw(40) << std::left << a << u << std::endl; // LCOV_EXCL_STOP } } template typename std::enable_if::value>::type check_within_half(T a, U u) { BOOST_MATH_STD_USING if (upper(T(fabs(a - u))) > 0.5f) { // LCOV_EXCL_START BOOST_ERROR("Rounded result differed by more than 0.5 from the original"); std::cerr << "Values were: " << std::setprecision(35) << std::setw(40) << std::left << a << u << std::endl; // LCOV_EXCL_STOP } if ((upper(T(fabs(a - u))) == 0.5f) && (fabs(static_cast(u)) < fabs(a))) { // LCOV_EXCL_START BOOST_ERROR("Rounded result was towards zero with boost::round"); std::cerr << "Values were: " << std::setprecision(35) << std::setw(40) << std::left << a << u << std::endl; // LCOV_EXCL_STOP } } // // We may not have an abs overload for long long so provide a fall back: // inline unsigned safe_abs(int const& v) { return v < 0 ? static_cast(1u) + static_cast(-(v + 1)) : v; } inline unsigned long safe_abs(long const& v) { return v < 0 ? static_cast(1u) + static_cast(-(v + 1)) : v; } inline unsigned long long safe_abs(long long const& v) { return v < 0 ? static_cast(1u) + static_cast(-(v + 1)) : v; } template inline typename std::enable_if::value, T>::type safe_abs(T const& v) { return v < 0 ? -v : v; } template void check_trunc_result(T a, U u) { BOOST_MATH_STD_USING if (fabs(a - u) >= 1) { // LCOV_EXCL_START BOOST_ERROR("Rounded result differed by more than 1 from the original"); std::cerr << "Values were: " << std::setprecision(35) << std::setw(40) << std::left << a << u << std::endl; // LCOV_EXCL_STOP } if (abs(a) < safe_abs(u)) { // LCOV_EXCL_START BOOST_ERROR("Truncated result had larger absolute value than the original"); std::cerr << "Values were: " << std::setprecision(35) << std::setw(40) << std::left << abs(a) << safe_abs(u) << std::endl; // LCOV_EXCL_STOP } if (fabs(static_cast(u)) > fabs(a)) { // LCOV_EXCL_START BOOST_ERROR("Rounded result was away from zero with boost::trunc"); std::cerr << "Values were: " << std::setprecision(35) << std::setw(40) << std::left << a << u << std::endl; // LCOV_EXCL_STOP } } template void test() { if (std::numeric_limits::digits >= std::numeric_limits::digits) { auto lhs = static_cast((std::numeric_limits::max)()); long k = lround(lhs); check_within_half(lhs, k); BOOST_TEST(k == lround(static_cast((std::numeric_limits::max)()) + 0)); k = lround(static_cast((std::numeric_limits::min)())); check_within_half(static_cast((std::numeric_limits::min)()), k); BOOST_TEST(k == lround(static_cast((std::numeric_limits::min)()) + 0)); k = ltrunc(static_cast((std::numeric_limits::max)())); check_trunc_result(static_cast((std::numeric_limits::max)()), k); BOOST_TEST(k == ltrunc(static_cast((std::numeric_limits::max)()) + 0)); k = ltrunc(static_cast((std::numeric_limits::min)())); check_trunc_result(static_cast((std::numeric_limits::min)()), k); BOOST_TEST(k == ltrunc(static_cast((std::numeric_limits::min)()) + 0)); lhs = static_cast((std::numeric_limits::max)() - 1); k = lround(lhs); check_within_half(lhs, k); k = lround(static_cast((std::numeric_limits::min)() + 1)); check_within_half(static_cast((std::numeric_limits::min)() + 1), k); k = ltrunc(static_cast((std::numeric_limits::max)() - 1)); check_trunc_result(static_cast((std::numeric_limits::max)() - 1), k); k = ltrunc(static_cast((std::numeric_limits::min)() + 1)); check_trunc_result(static_cast((std::numeric_limits::min)() + 1), k); // Further testing of the boundary since the upper end of long is not exactly representable by double for (long i = 2; i < 256; ++i) { lhs = static_cast((std::numeric_limits::max)() - i); k = lround(lhs); check_within_half(lhs, k); } } } int main() { test(); test(); // Older PPC64LE have ibm128 instead of ieee128 which is not compatible with this type #if !defined(__PPC__) test(); #endif return boost::report_errors(); }