// (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) #include #include #define BOOST_TEST_MAIN #include #include #include #include #include #include #include #include #include #include #include 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 ? -ldexp(frexp(result, &bits), exponent) : ldexp(frexp(result, &bits), exponent); } template void 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; } } // // We may not have an abs overload for long long so provide a fall back: // template inline T 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_round_number(T arg) { BOOST_MATH_STD_USING #ifdef BOOST_HAS_LONG_LONG using boost::math::llround; using boost::math::lltrunc; #endif T r = round(arg); check_within_half(arg, r); r = trunc(arg); check_trunc_result(arg, r); T frac = boost::math::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); i = itrunc(arg); check_trunc_result(arg, T(i)); r = boost::math::modf(arg, &i); check_modf_result(arg, r, i); } 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); si = iround(static_cast((std::numeric_limits::min)())); check_within_half(static_cast((std::numeric_limits::min)()), si); si = itrunc(static_cast((std::numeric_limits::max)())); check_trunc_result(static_cast((std::numeric_limits::max)()), T(si)); si = itrunc(static_cast((std::numeric_limits::min)())); check_trunc_result(static_cast((std::numeric_limits::min)()), T(si)); } if(abs(r) < (std::numeric_limits::max)()) { long l = lround(arg); check_within_half(arg, l); l = ltrunc(arg); check_trunc_result(arg, T(l)); r = boost::math::modf(arg, &l); check_modf_result(arg, r, l); } 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); k = lround(static_cast((std::numeric_limits::min)())); check_within_half(static_cast((std::numeric_limits::min)()), k); k = ltrunc(static_cast((std::numeric_limits::max)())); check_trunc_result(static_cast((std::numeric_limits::max)()), T(k)); k = ltrunc(static_cast((std::numeric_limits::min)())); check_trunc_result(static_cast((std::numeric_limits::min)()), T(k)); } #ifdef BOOST_HAS_LONG_LONG if(abs(r) < (std::numeric_limits::max)()) { boost::long_long_type ll = llround(arg); check_within_half(arg, ll); ll = lltrunc(arg); check_trunc_result(arg, T(ll)); r = boost::math::modf(arg, &ll); check_modf_result(arg, r, ll); } 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); j = llround(static_cast((std::numeric_limits::min)())); check_within_half(static_cast((std::numeric_limits::min)()), j); j = lltrunc(static_cast((std::numeric_limits::max)())); check_trunc_result(static_cast((std::numeric_limits::max)()), T(j)); j = lltrunc(static_cast((std::numeric_limits::min)())); check_trunc_result(static_cast((std::numeric_limits::min)()), T(j)); } #endif } template void test_round(T, const char* name ) { BOOST_MATH_STD_USING #ifdef BOOST_HAS_LONG_LONG using boost::math::llround; using boost::math::lltrunc; #endif std::cout << "Testing rounding with type " << name << std::endl; for(int i = 0; i < 1000; ++i) { T arg = get_random(); test_round_number(arg); } // // Finish off by testing the error handlers: // BOOST_MATH_CHECK_THROW(iround(static_cast(1e20)), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(iround(static_cast(-1e20)), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(lround(static_cast(1e20)), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(lround(static_cast(-1e20)), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_MATH_CHECK_THROW(llround(static_cast(1e20)), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(llround(static_cast(-1e20)), boost::math::rounding_error); #endif if(std::numeric_limits::has_infinity) { BOOST_MATH_CHECK_THROW(round(std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(iround(std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(iround(-std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(lround(std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(lround(-std::numeric_limits::infinity()), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_MATH_CHECK_THROW(llround(std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(llround(-std::numeric_limits::infinity()), boost::math::rounding_error); #endif } if(std::numeric_limits::has_quiet_NaN) { BOOST_MATH_CHECK_THROW(round(std::numeric_limits::quiet_NaN()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(iround(std::numeric_limits::quiet_NaN()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(lround(std::numeric_limits::quiet_NaN()), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_MATH_CHECK_THROW(llround(std::numeric_limits::quiet_NaN()), boost::math::rounding_error); #endif } BOOST_MATH_CHECK_THROW(itrunc(static_cast(1e20)), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(itrunc(static_cast(-1e20)), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(ltrunc(static_cast(1e20)), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(ltrunc(static_cast(-1e20)), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_MATH_CHECK_THROW(lltrunc(static_cast(1e20)), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(lltrunc(static_cast(-1e20)), boost::math::rounding_error); #endif if(std::numeric_limits::has_infinity) { BOOST_MATH_CHECK_THROW(trunc(std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(itrunc(std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(itrunc(-std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(ltrunc(std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(ltrunc(-std::numeric_limits::infinity()), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_MATH_CHECK_THROW(lltrunc(std::numeric_limits::infinity()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(lltrunc(-std::numeric_limits::infinity()), boost::math::rounding_error); #endif } if(std::numeric_limits::has_quiet_NaN) { BOOST_MATH_CHECK_THROW(trunc(std::numeric_limits::quiet_NaN()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(itrunc(std::numeric_limits::quiet_NaN()), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(ltrunc(std::numeric_limits::quiet_NaN()), boost::math::rounding_error); #ifdef BOOST_HAS_LONG_LONG BOOST_MATH_CHECK_THROW(lltrunc(std::numeric_limits::quiet_NaN()), boost::math::rounding_error); #endif } if(std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_MATH_CHECK_THROW(itrunc(static_cast((std::numeric_limits::max)()) + 1), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(itrunc(static_cast((std::numeric_limits::min)()) - 1), boost::math::rounding_error); } if(std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_MATH_CHECK_THROW(ltrunc(static_cast((std::numeric_limits::max)()) + 1), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(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_MATH_CHECK_THROW(lltrunc(static_cast((std::numeric_limits::max)()) + 1), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(lltrunc(static_cast((std::numeric_limits::min)()) - 1), boost::math::rounding_error); } #endif if(std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_MATH_CHECK_THROW(iround(static_cast((std::numeric_limits::max)()) + 1), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(iround(static_cast((std::numeric_limits::min)()) - 1), boost::math::rounding_error); } if(std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_MATH_CHECK_THROW(lround(static_cast((std::numeric_limits::max)()) + 1), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(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_MATH_CHECK_THROW(llround(static_cast((std::numeric_limits::max)()) + 1), boost::math::rounding_error); BOOST_MATH_CHECK_THROW(llround(static_cast((std::numeric_limits::min)()) - 1), boost::math::rounding_error); } #endif // // try non-throwing error handlers: // boost::math::policies::policy > pol; if(std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_EQUAL(iround((std::numeric_limits::max)() + T(1.0), pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(iround((std::numeric_limits::min)() - T(1.0), pol), (std::numeric_limits::min)()); BOOST_CHECK_EQUAL(itrunc((std::numeric_limits::max)() + T(1.0), pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(itrunc((std::numeric_limits::min)() - T(1.0), pol), (std::numeric_limits::min)()); } if(std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_EQUAL(lround((std::numeric_limits::max)() + T(1.0), pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(lround((std::numeric_limits::min)() - T(1.0), pol), (std::numeric_limits::min)()); BOOST_CHECK_EQUAL(ltrunc((std::numeric_limits::max)() + T(1.0), pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(ltrunc((std::numeric_limits::min)() - T(1.0), pol), (std::numeric_limits::min)()); } #ifndef BOOST_NO_LONG_LONG if(std::numeric_limits::digits >= std::numeric_limits::digits) { BOOST_CHECK_EQUAL(llround((std::numeric_limits::max)() + T(1.0), pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(llround((std::numeric_limits::min)() - T(1.0), pol), (std::numeric_limits::min)()); BOOST_CHECK_EQUAL(lltrunc((std::numeric_limits::max)() + T(1.0), pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(lltrunc((std::numeric_limits::min)() - T(1.0), pol), (std::numeric_limits::min)()); } #endif // Again with bigger value: T big = 1e20f; BOOST_CHECK_EQUAL(iround(big, pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(lround(big, pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(iround(-big, pol), (std::numeric_limits::min)()); BOOST_CHECK_EQUAL(lround(-big, pol), (std::numeric_limits::min)()); BOOST_CHECK_EQUAL(itrunc(big, pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(ltrunc(big, pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(itrunc(-big, pol), (std::numeric_limits::min)()); BOOST_CHECK_EQUAL(ltrunc(-big, pol), (std::numeric_limits::min)()); #ifndef BOOST_NO_LONG_LONG BOOST_CHECK_EQUAL(llround(big, pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(llround(-big, pol), (std::numeric_limits::min)()); BOOST_CHECK_EQUAL(lltrunc(big, pol), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(lltrunc(-big, pol), (std::numeric_limits::min)()); #endif // // Special cases that we know can go bad: // T half = 0.5f; half = boost::math::float_prior(half); test_round_number(half); half = -0.5f; half = boost::math::float_next(half); test_round_number(half); if(std::numeric_limits::is_specialized) { // // Odd and even integer values: // T val; for(int i = 2; i < std::numeric_limits::max_exponent; ++i) { val = ldexp(T(1), i); test_round_number(val); ++val; test_round_number(val); val = -val; test_round_number(val); ++val; test_round_number(val); } } } BOOST_AUTO_TEST_CASE( test_main ) { test_round(0.1F, "float"); test_round(0.1, "double"); #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS test_round(0.1L, "long double"); test_round(boost::math::concepts::real_concept(0.1), "real_concept"); #else std::cout << "The long double tests have been disabled on this platform " "either because the long double overloads of the usual math functions are " "not available at all, or because they are too inaccurate for these tests " "to pass." << std::endl; #endif // test rounding of direct predecessor/successor of 0.5/-0.5 for float and double test_round_number(-0.4999999701976776123046875f); BOOST_CHECK_EQUAL(boost::math::round(-0.4999999701976776123046875f), 0.0f); test_round_number(0.4999999701976776123046875f); BOOST_CHECK_EQUAL(boost::math::round(0.4999999701976776123046875f), 0.0f); BOOST_CHECK_EQUAL(boost::math::round(-0.499999999999999944488848768742172978818416595458984375), 0.0); test_round_number(-0.499999999999999944488848768742172978818416595458984375); BOOST_CHECK_EQUAL(boost::math::round(0.499999999999999944488848768742172978818416595458984375), 0.0); test_round_number(0.499999999999999944488848768742172978818416595458984375); // test rounding of integer numbers on the edge of the float/double mantissa width BOOST_CHECK_EQUAL(boost::math::round(-16777215.0f), -16777215.0f); test_round_number(-16777215.0f); BOOST_CHECK_EQUAL(boost::math::round(-16777213.0f), -16777213.0f); test_round_number(-16777213.0f); BOOST_CHECK_EQUAL(boost::math::round(-8388611.0f), -8388611.0f); test_round_number(-8388611.0f); BOOST_CHECK_EQUAL(boost::math::round(-8388609.0f), -8388609.0f); test_round_number(-8388609.0f); BOOST_CHECK_EQUAL(boost::math::round(8388609.0f), 8388609.0f); test_round_number(8388609.0f); BOOST_CHECK_EQUAL(boost::math::round(8388611.0f), 8388611.0f); test_round_number(8388611.0f); BOOST_CHECK_EQUAL(boost::math::round(16777213.0f), 16777213.0f); test_round_number(16777213.0f); BOOST_CHECK_EQUAL(boost::math::round(16777215.0f), 16777215.0f); test_round_number(16777215.0f); BOOST_CHECK_EQUAL(boost::math::round(-9007199254740993.0), -9007199254740993.0); test_round_number(-9007199254740993.0); BOOST_CHECK_EQUAL(boost::math::round(-9007199254740991.0), -9007199254740991.0); test_round_number(-9007199254740991.0); BOOST_CHECK_EQUAL(boost::math::round(-4503599627370499.0), -4503599627370499.0); test_round_number(-4503599627370499.0); BOOST_CHECK_EQUAL(boost::math::round(-4503599627370497.0), -4503599627370497.0); test_round_number(-4503599627370497.0); BOOST_CHECK_EQUAL(boost::math::round(4503599627370497.0), 4503599627370497.0); test_round_number(4503599627370497.0); BOOST_CHECK_EQUAL(boost::math::round(4503599627370499.0), 4503599627370499.0); test_round_number(4503599627370499.0); BOOST_CHECK_EQUAL(boost::math::round(9007199254740991.0), 9007199254740991.0); test_round_number(9007199254740991.0); BOOST_CHECK_EQUAL(boost::math::round(9007199254740993.0), 9007199254740993.0); test_round_number(9007199254740993.0); }