// Copyright John Maddock 2013. // 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" #include #include #include #ifdef BOOST_MSVC #pragma warning(disable:4127) #endif template struct stopwatch { typedef typename Clock::duration duration; stopwatch() { m_start = Clock::now(); } duration elapsed() { return Clock::now() - m_start; } void reset() { m_start = Clock::now(); } private: typename Clock::time_point m_start; }; template struct exponent_type { typedef int type; }; template struct exponent_type > { typedef typename T::exponent_type type; }; template T generate_random_float() { BOOST_MATH_STD_USING typedef typename exponent_type::type e_type; static boost::random::mt19937 gen; T val = gen(); T prev_val = -1; while(val != prev_val) { val *= (gen.max)(); prev_val = val; val += gen(); } e_type e; val = frexp(val, &e); static boost::random::uniform_int_distribution ui(0, std::numeric_limits::max_exponent - std::numeric_limits::digits - 20); return ldexp(val, ui(gen)); } template void do_round_trip(const T& val) { boost::multiprecision::cpp_rational rat(val); T new_f(rat); BOOST_CHECK_EQUAL(val, new_f); // // Try adding or subtracting an insignificant amount // (0.25ulp) from rat and check that it rounds to the same value: // typename exponent_type::type e; frexp(val, &e); e -= std::numeric_limits::digits + 2; boost::multiprecision::cpp_rational delta, rounded; boost::multiprecision::cpp_int i(1); i <<= (e < 0 ? -e : e); if(e > 0) delta.assign(i); else delta = boost::multiprecision::cpp_rational(boost::multiprecision::cpp_int(1), i); rounded = rat + delta; new_f = static_cast(rounded); BOOST_CHECK_EQUAL(val, new_f); rounded = rat - delta; new_f = static_cast(rounded); BOOST_CHECK_EQUAL(val, new_f); delta /= 2; rounded = rat + delta; new_f = static_cast(rounded); BOOST_CHECK_EQUAL(val, new_f); rounded = rat - delta; new_f = static_cast(rounded); BOOST_CHECK_EQUAL(val, new_f); delta /= 2; rounded = rat + delta; new_f = static_cast(rounded); BOOST_CHECK_EQUAL(val, new_f); rounded = rat - delta; new_f = static_cast(rounded); BOOST_CHECK_EQUAL(val, new_f); } template void test_round_trip() { std::cout << "Testing type " << typeid(T).name() << std::endl; std::cout << "digits = " << std::numeric_limits::digits << std::endl; std::cout << "digits10 = " << std::numeric_limits::digits10 << std::endl; std::cout << "max_digits10 = " << std::numeric_limits::max_digits10 << std::endl; stopwatch w; int count = 0; while(boost::chrono::duration_cast >(w.elapsed()).count() < 200) { T val = generate_random_float(); do_round_trip(val); do_round_trip(T(-val)); do_round_trip(T(1/val)); do_round_trip(T(-1/val)); count += 4; } std::cout << "Execution time = " << boost::chrono::duration_cast >(w.elapsed()).count() << "s" << std::endl; std::cout << "Total values tested: " << count << std::endl; } boost::multiprecision::cpp_int generate_random_int() { static boost::random::mt19937 gen; static boost::random::uniform_int_distribution d(1, 20); int lim; boost::multiprecision::cpp_int cppi(0); lim = d(gen); for(int i = 0; i < lim; ++i) { cppi *= (gen.max)(); cppi += gen(); } return cppi; } template void test_random_rationals() { stopwatch w; int count = 0; while(boost::chrono::duration_cast>(w.elapsed()).count() < 200) { boost::multiprecision::cpp_rational rat(generate_random_int(), generate_random_int()); Float f(rat); boost::multiprecision::cpp_rational new_rat(f); // rounded value int c = new_rat.compare(rat); if(c < 0) { // If f was rounded down, next float up must be above the original value: f = boost::math::float_next(f); new_rat.assign(f); BOOST_CHECK(new_rat >= rat); } else if(c > 0) { // If f was rounded up, next float down must be below the original value: f = boost::math::float_prior(f); new_rat.assign(f); BOOST_CHECK(new_rat <= rat); } else { // Values were equal... nothing to test. } } std::cout << "Execution time = " << boost::chrono::duration_cast >(w.elapsed()).count() << "s" << std::endl; std::cout << "Total values tested: " << count << std::endl; } int main() { using namespace boost::multiprecision; #if defined(TEST1) && !defined(BOOST_MSVC) test_round_trip > >(); #elif defined(TEST2) test_round_trip(); #elif defined(TEST3) && !defined(BOOST_MSVC) test_random_rationals > >(); #elif defined(TEST4) test_random_rationals(); #endif return boost::report_errors(); }