/////////////////////////////////////////////////////////////////////////////// // Copyright 2021 Fahad Syed. // Copyright 2021 Christopher Kormanyos. // Copyright 2021 Janek Kozicki. // Distributed under 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) // // Test for correctness of arithmetic operators of cpp_double_float<> #include #include #include #include #ifdef BOOST_MATH_USE_FLOAT128 #include #endif #include #include #include #include #include #include namespace test_arithmetic_cpp_double_float { // FIXME: this looks like a duplicate from test_cpp_double_float_comparision.cpp file. template struct is_floating_point { static const bool value; }; template const bool is_floating_point::value = std::is_floating_point::value #ifdef BOOST_MATH_USE_FLOAT128 or std::is_same::value #endif ; template ::value, bool>::type = true> FloatingPointType uniform_real() { static std::random_device rd; static std::mt19937 gen (rd()); static boost::random::uniform_real_distribution dis(0.0, 1.0); return dis(gen); } int rand_in_range(int a, int b) { return a + int(float(b - a) * uniform_real()); } template ::value, bool>::type = true> FloatingPointType uniform_rand() { return uniform_real(); } template boost::multiprecision::backends::cpp_double_float uniform_rand() { using float_type = typename FloatingPointType::float_type; return boost::multiprecision::backends::cpp_double_float(uniform_real()) * boost::multiprecision::backends::cpp_double_float(uniform_real()); } template ::value>::type const* = nullptr> FloatingPointType log_rand() { if (uniform_real() < (1. / 100.)) return 0; // throw in a few zeroes using std::ldexp; FloatingPointType ret = ldexp(uniform_real(), rand_in_range(std::numeric_limits::min_exponent, std::numeric_limits::max_exponent)); using std::fmax; return fmax(ret, std::numeric_limits::epsilon()); } template boost::multiprecision::backends::cpp_double_float log_rand() { boost::multiprecision::backends::cpp_double_float a(uniform_rand >() + typename FloatingPointType::float_type(1)); a *= log_rand(); return a; } template ::is_iec559>::type const* = nullptr> ConstructionType construct_from(FloatingPointType f) { return ConstructionType(f); } template ::is_iec559>::type const* = nullptr> ConstructionType construct_from(FloatingPointType f) { return ConstructionType(f.first()) + ConstructionType(f.second()); } template int test_op(char op, const unsigned count = 10000U) { using naked_double_float_type = FloatingPointType; using control_float_type = boost::multiprecision::number::digits10 * 2 + 1>, boost::multiprecision::et_off>; const control_float_type MaxError = boost::multiprecision::ldexp(control_float_type(1), -std::numeric_limits::digits); std::cout << "testing operator" << op << " (accuracy = " << std::numeric_limits::digits << " bits)..."; for (unsigned i = 0U; i < count; ++i) { naked_double_float_type df_a = log_rand(); naked_double_float_type df_b = log_rand(); const control_float_type ctrl_a = construct_from(df_a); const control_float_type ctrl_b = construct_from(df_b); naked_double_float_type df_c; control_float_type ctrl_c; switch (op) { case '+': df_c = df_a + df_b; ctrl_c = ctrl_a + ctrl_b; break; case '-': df_c = df_a - df_b; ctrl_c = ctrl_a - ctrl_b; break; case '*': df_c = df_a * df_b; ctrl_c = ctrl_a * ctrl_b; break; case '/': if (df_b == naked_double_float_type(0)) continue; df_c = df_a / df_b; ctrl_c = ctrl_a / ctrl_b; break; default: std::cerr << " internal error (unknown operator: " << op << ")" << std::endl; return -1; } // if exponent of result is out of range, continue int exp2; boost::multiprecision::frexp(ctrl_c, &exp2); if (exp2 > std::numeric_limits::max_exponent || exp2 < std::numeric_limits::min_exponent) continue; control_float_type ctrl_df_c = construct_from(df_c); const auto delta = fabs(1 - fabs(ctrl_c / ctrl_df_c)); if (delta > MaxError) { std::cerr << std::setprecision(std::numeric_limits::digits10 + 2); std::cerr << " [FAILED] while performing '" << std::setprecision(100000) << ctrl_a << "' " << op << " '" << ctrl_b << "', got incorrect result: " << (df_c) << std::endl; // uncomment for more debugging information (only for cpp_double_float<> type) //std::cerr << "(df_a = " << df_a.get_raw_str() << ", df_b = " << df_b.get_raw_str() << ")" << std::endl; //std::cerr << "expected: " << ctrl_c << std::endl; //std::cerr << "actual : " << ctrl_df_c << " (" << df_c.get_raw_str() << ")" << std::endl; //std::cerr << "error : " << delta << std::endl; return -1; } } std::cout << " ok [" << count << " tests passed]" << std::endl; return 0; } template bool test_arithmetic() { std::cout << "Testing correctness of arithmetic operators for " << boost::core::demangle(typeid(T).name()) << std::endl; int e = 0; e += test_op('+'); e += test_op('-'); e += test_op('*'); e += test_op('/'); std::cout << std::endl; return e; } } // namespace test_arithmetic_cpp_double_float int main() { int e = 0; // uncomment to check if tests themselves are correct e += test_arithmetic_cpp_double_float::test_arithmetic(); e += test_arithmetic_cpp_double_float::test_arithmetic(); e += test_arithmetic_cpp_double_float::test_arithmetic(); #ifdef BOOST_MATH_USE_FLOAT128 e += test_arithmetic_cpp_double_float::test_arithmetic(); #endif e += test_arithmetic_cpp_double_float::test_arithmetic >(); e += test_arithmetic_cpp_double_float::test_arithmetic >(); e += test_arithmetic_cpp_double_float::test_arithmetic >(); #ifdef BOOST_MATH_USE_FLOAT128 e += test_arithmetic_cpp_double_float::test_arithmetic >(); #endif return e; }