// (C) Copyright John Maddock 2007. // 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) #ifdef _MSC_VER #define _SCL_SECURE_NO_WARNINGS #endif #include #include #include #include #include #include #include "test.hpp" #if !defined(TEST_MPF_50) && !defined(TEST_MPF) && !defined(TEST_BACKEND) && !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR) && !defined(TEST_MPFR_50) && !defined(TEST_MPFI_50) && !defined(TEST_FLOAT128) && !defined(TEST_CPP_BIN_FLOAT) #define TEST_MPF_50 #define TEST_MPFR_50 #define TEST_MPFI_50 #define TEST_BACKEND #define TEST_CPP_DEC_FLOAT #define TEST_FLOAT128 #define TEST_CPP_BIN_FLOAT #ifdef _MSC_VER #pragma message("CAUTION!!: No backend type specified so testing everything.... this will take some time!!") #endif #ifdef __GNUC__ #pragma warning "CAUTION!!: No backend type specified so testing everything.... this will take some time!!" #endif #endif #if defined(TEST_MPF_50) #include #endif #ifdef TEST_MPFR_50 #include #endif #ifdef TEST_MPFI_50 #include #endif #ifdef TEST_BACKEND #include #endif #ifdef TEST_CPP_DEC_FLOAT #include #endif #ifdef TEST_CPP_BIN_FLOAT #include #endif #ifdef TEST_FLOAT128 #include #endif #ifdef BOOST_MSVC #pragma warning(disable : 4127) #endif boost::mt19937 rng; template T get_random() { // // Fill all the bits in T with random values, // likewise set the exponent to a random value // that will still fit inside a T, and always // have a remainder as well as an integer part. // int bits = boost::math::tools::digits(); int shift = 0; int exponent = rng() % (bits - 4); T result = 0; while (bits > 0) { result += ldexp(static_cast(rng()), shift); shift += std::numeric_limits::digits; bits -= std::numeric_limits::digits; } return rng() & 1u ? T(-ldexp(frexp(result, &bits), exponent)) : T(ldexp(frexp(result, &bits), exponent)); } template typename boost::disable_if_c::value>::type check_within_half(T a, U u) { BOOST_MATH_STD_USING if (fabs(a - u) > 0.5f) { 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; } if ((fabs(a - u) == 0.5f) && (fabs(static_cast(u)) < fabs(a))) { 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; } } template typename boost::enable_if_c::value>::type check_within_half(T a, U u) { BOOST_MATH_STD_USING if (upper(T(fabs(a - u))) > 0.5f) { 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; } if ((upper(T(fabs(a - u))) == 0.5f) && (fabs(static_cast(u)) < fabs(a))) { 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; } } // // 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 boost::disable_if_c::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) { 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; } if (abs(a) < safe_abs(u)) { BOOST_ERROR("Truncated result had larger absolute value than the original"); std::cerr << "Values were: " << std::setprecision(35) << std::setw(40) << std::left << a << u << std::endl; } if (fabs(static_cast(u)) > fabs(a)) { 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; } } template void check_modf_result(T a, T fract, U ipart) { BOOST_MATH_STD_USING if (fract + ipart != a) { BOOST_ERROR("Fractional and integer results do not add up to the original value"); std::cerr << "Values were: " << std::setprecision(35) << " " << std::left << a << ipart << " " << fract << std::endl; } if ((boost::math::sign(a) != boost::math::sign(fract)) && boost::math::sign(fract)) { BOOST_ERROR("Original and fractional parts have differing signs"); std::cerr << "Values were: " << std::setprecision(35) << " " << std::left << a << ipart << " " << fract << std::endl; } if ((boost::math::sign(a) != boost::math::sign(ipart)) && boost::math::sign(ipart)) { BOOST_ERROR("Original and integer parts have differing signs"); std::cerr << "Values were: " << std::setprecision(35) << " " << std::left << a << ipart << " " << ipart << std::endl; } if (fabs(a - ipart) >= 1) { 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 << ipart << std::endl; } } template void test() { BOOST_MATH_STD_USING for (int i = 0; i < 1000; ++i) { T arg = get_random(); T r = round(arg); check_within_half(arg, r); BOOST_TEST(r == round(arg + 0)); r = trunc(arg); check_trunc_result(arg, r); BOOST_TEST(r == trunc(arg + 0)); T frac = modf(arg, &r); check_modf_result(arg, frac, r); if (abs(r) < (std::numeric_limits::max)()) { int i = iround(arg); check_within_half(arg, i); BOOST_TEST(i == iround(arg + 0)); i = itrunc(arg); check_trunc_result(arg, i); BOOST_TEST(i == itrunc(arg + 0)); r = modf(arg, &i); check_modf_result(arg, r, i); } if (abs(r) < (std::numeric_limits::max)()) { long l = lround(arg); check_within_half(arg, l); BOOST_TEST(l == lround(arg + 0)); l = ltrunc(arg); check_trunc_result(arg, l); BOOST_TEST(l == ltrunc(arg + 0)); r = modf(arg, &l); check_modf_result(arg, r, l); } #ifdef BOOST_HAS_LONG_LONG if (abs(r) < (std::numeric_limits::max)()) { boost::long_long_type ll = llround(arg); check_within_half(arg, ll); BOOST_TEST(ll == llround(arg + 0)); ll = lltrunc(arg); check_trunc_result(arg, ll); BOOST_TEST(ll == lltrunc(arg + 0)); r = modf(arg, &ll); check_modf_result(arg, r, ll); } #endif } // // Test boundary cases: // if (std::numeric_limits::digits >= std::numeric_limits::digits) { int si = iround(static_cast((std::numeric_limits::max)())); check_within_half(static_cast((std::numeric_limits::max)()), si); BOOST_TEST(si == iround(static_cast((std::numeric_limits::max)()) + 0)); si = iround(static_cast((std::numeric_limits::min)())); check_within_half(static_cast((std::numeric_limits::min)()), si); BOOST_TEST(si == iround(static_cast((std::numeric_limits::min)()) + 0)); si = itrunc(static_cast((std::numeric_limits::max)())); check_trunc_result(static_cast((std::numeric_limits::max)()), si); BOOST_TEST(si == itrunc(static_cast((std::numeric_limits::max)()) + 0)); si = itrunc(static_cast((std::numeric_limits::min)())); check_trunc_result(static_cast((std::numeric_limits::min)()), si); BOOST_TEST(si == itrunc(static_cast((std::numeric_limits::min)()) + 0)); si = iround(static_cast((std::numeric_limits::max)() - 1)); check_within_half(static_cast((std::numeric_limits::max)() - 1), si); si = iround(static_cast((std::numeric_limits::min)() + 1)); check_within_half(static_cast((std::numeric_limits::min)() + 1), si); si = itrunc(static_cast((std::numeric_limits::max)() - 1)); check_trunc_result(static_cast((std::numeric_limits::max)() - 1), si); si = itrunc(static_cast((std::numeric_limits::min)() + 1)); check_trunc_result(static_cast((std::numeric_limits::min)() + 1), si); } if (std::numeric_limits::digits >= std::numeric_limits::digits) { long k = lround(static_cast((std::numeric_limits::max)())); check_within_half(static_cast((std::numeric_limits::max)()), 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)); k = lround(static_cast((std::numeric_limits::max)() - 1)); check_within_half(static_cast((std::numeric_limits::max)() - 1), 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); } #ifndef BOOST_NO_LONG_LONG if (std::numeric_limits::digits >= std::numeric_limits::digits) { boost::long_long_type j = llround(static_cast((std::numeric_limits::max)())); check_within_half(static_cast((std::numeric_limits::max)()), j); BOOST_TEST(j == llround(static_cast((std::numeric_limits::max)()) + 0)); j = llround(static_cast((std::numeric_limits::min)())); check_within_half(static_cast((std::numeric_limits::min)()), j); BOOST_TEST(j == llround(static_cast((std::numeric_limits::min)()) + 0)); j = lltrunc(static_cast((std::numeric_limits::max)())); check_trunc_result(static_cast((std::numeric_limits::max)()), j); BOOST_TEST(j == lltrunc(static_cast((std::numeric_limits::max)()) + 0)); j = lltrunc(static_cast((std::numeric_limits::min)())); check_trunc_result(static_cast((std::numeric_limits::min)()), j); BOOST_TEST(j == lltrunc(static_cast((std::numeric_limits::min)()) + 0)); j = llround(static_cast((std::numeric_limits::max)() - 1)); check_within_half(static_cast((std::numeric_limits::max)() - 1), j); j = llround(static_cast((std::numeric_limits::min)() + 1)); check_within_half(static_cast((std::numeric_limits::min)() + 1), j); j = lltrunc(static_cast((std::numeric_limits::max)() - 1)); check_trunc_result(static_cast((std::numeric_limits::max)() - 1), j); j = lltrunc(static_cast((std::numeric_limits::min)() + 1)); check_trunc_result(static_cast((std::numeric_limits::min)() + 1), j); } #endif // // Finish off by testing the error handlers: // T result; #ifndef BOOST_NO_EXCEPTIONS BOOST_CHECK_THROW(result = static_cast(iround(static_cast(1e20))), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(iround(static_cast(-1e20))), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lround(static_cast(1e20))), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lround(static_cast(-1e20))), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_CHECK_THROW(result = static_cast(llround(static_cast(1e20))), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(llround(static_cast(-1e20))), boost::math::rounding_error); #endif if (std::numeric_limits::has_infinity) { BOOST_CHECK_EQUAL(static_cast(round(std::numeric_limits::infinity())), std::numeric_limits::infinity()); // See C99 Annex F. BOOST_CHECK_THROW(result = static_cast(iround(std::numeric_limits::infinity())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(iround(-std::numeric_limits::infinity())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lround(std::numeric_limits::infinity())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lround(-std::numeric_limits::infinity())), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_CHECK_THROW(result = static_cast(llround(std::numeric_limits::infinity())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(llround(-std::numeric_limits::infinity())), boost::math::rounding_error); #endif } if (std::numeric_limits::has_quiet_NaN) { BOOST_CHECK((boost::multiprecision::isnan)(round(std::numeric_limits::quiet_NaN()))); BOOST_CHECK_THROW(result = static_cast(iround(std::numeric_limits::quiet_NaN())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lround(std::numeric_limits::quiet_NaN())), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_CHECK_THROW(result = static_cast(llround(std::numeric_limits::quiet_NaN())), boost::math::rounding_error); #endif } BOOST_CHECK_THROW(result = static_cast(itrunc(static_cast(1e20))), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(itrunc(static_cast(-1e20))), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(ltrunc(static_cast(1e20))), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(ltrunc(static_cast(-1e20))), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_CHECK_THROW(result = static_cast(lltrunc(static_cast(1e20))), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lltrunc(static_cast(-1e20))), boost::math::rounding_error); #endif if (std::numeric_limits::has_infinity) { BOOST_CHECK_EQUAL(static_cast(trunc(std::numeric_limits::infinity())), std::numeric_limits::infinity()); BOOST_CHECK_EQUAL(static_cast(trunc(-std::numeric_limits::infinity())), -std::numeric_limits::infinity()); BOOST_CHECK_THROW(result = static_cast(itrunc(std::numeric_limits::infinity())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(itrunc(-std::numeric_limits::infinity())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(ltrunc(std::numeric_limits::infinity())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(ltrunc(-std::numeric_limits::infinity())), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_CHECK_THROW(result = static_cast(lltrunc(std::numeric_limits::infinity())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lltrunc(-std::numeric_limits::infinity())), boost::math::rounding_error); #endif } if (std::numeric_limits::has_quiet_NaN) { BOOST_CHECK((boost::multiprecision::isnan)(trunc(std::numeric_limits::quiet_NaN()))); BOOST_CHECK_THROW(result = static_cast(itrunc(std::numeric_limits::quiet_NaN())), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(ltrunc(std::numeric_limits::quiet_NaN())), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_CHECK_THROW(result = static_cast(lltrunc(std::numeric_limits::quiet_NaN())), boost::math::rounding_error); #endif } if (std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_THROW(result = static_cast(itrunc(static_cast((std::numeric_limits::max)()) + 1)), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(itrunc(static_cast((std::numeric_limits::min)()) - 1)), boost::math::rounding_error); } if (std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_THROW(result = static_cast(ltrunc(static_cast((std::numeric_limits::max)()) + 1)), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(ltrunc(static_cast((std::numeric_limits::min)()) - 1)), boost::math::rounding_error); } #ifndef BOOST_NO_LONG_LONG if (std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_THROW(result = static_cast(lltrunc(static_cast((std::numeric_limits::max)()) + 1)), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lltrunc(static_cast((std::numeric_limits::min)()) - 1)), boost::math::rounding_error); } #endif if (std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_THROW(result = static_cast(iround(static_cast((std::numeric_limits::max)()) + 1)), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(iround(static_cast((std::numeric_limits::min)()) - 1)), boost::math::rounding_error); } if (std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_THROW(result = static_cast(lround(static_cast((std::numeric_limits::max)()) + 1)), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(lround(static_cast((std::numeric_limits::min)()) - 1)), boost::math::rounding_error); } #ifndef BOOST_NO_LONG_LONG if (std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_THROW(result = static_cast(llround(static_cast((std::numeric_limits::max)()) + 1)), boost::math::rounding_error); BOOST_CHECK_THROW(result = static_cast(llround(static_cast((std::numeric_limits::min)()) - 1)), boost::math::rounding_error); } #endif #endif } int main() { #ifdef TEST_MPF_50 test(); test(); #endif #ifdef TEST_MPFR_50 test(); test(); #endif #ifdef TEST_MPFI_50 test(); test(); #endif #ifdef TEST_CPP_DEC_FLOAT test(); test(); #ifndef SLOW_COMPLER // Some "peculiar" digit counts which stress our code: test > >(); test > >(); test > >(); test > >(); test > >(); test > >(); test > > >(); test > > >(); #endif #endif #ifdef TEST_CPP_BIN_FLOAT test(); test(); test, boost::long_long_type> > >(); #endif #ifdef TEST_BACKEND test >(); #endif #ifdef TEST_FLOAT128 test(); #endif return boost::report_errors(); }