Files
multiprecision/test/test_pow.cpp
2025-06-27 15:28:52 +02:00

232 lines
8.0 KiB
C++

///////////////////////////////////////////////////////////////
// Copyright 2011 - 2025 John Maddock.
// Copyright Christopher Kormanyos 2021 - 2025.
// Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
//
// This work is based on an earlier work:
// "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations",
// in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469
#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#endif
#include <test.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <boost/math/special_functions/fpclassify.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) && !defined(TEST_CPP_DOUBLE_FLOAT)
#define TEST_MPF_50
//# define TEST_MPF
#define TEST_BACKEND
#define TEST_CPP_DEC_FLOAT
#define TEST_MPFR_50
#define TEST_MPFI_50
#define TEST_CPP_BIN_FLOAT
#define TEST_CPP_DOUBLE_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
#include <test_traits.hpp> // Note: include this AFTER the test-backends are defined
#include <test_pow_data.hpp>
#include <test_pow_data_reduced.hpp>
#if defined(TEST_MPF_50)
#include <boost/multiprecision/gmp.hpp>
#endif
#if defined(TEST_MPFR_50)
#include <boost/multiprecision/mpfr.hpp>
#endif
#if defined(TEST_MPFI_50)
#include <boost/multiprecision/mpfi.hpp>
#endif
#ifdef TEST_BACKEND
#include <boost/multiprecision/concepts/mp_number_archetypes.hpp>
#endif
#ifdef TEST_CPP_DEC_FLOAT
#include <boost/multiprecision/cpp_dec_float.hpp>
#endif
#ifdef TEST_FLOAT128
#include <boost/multiprecision/float128.hpp>
#endif
#ifdef TEST_CPP_BIN_FLOAT
#include <boost/multiprecision/cpp_bin_float.hpp>
#endif
#ifdef TEST_CPP_DOUBLE_FLOAT
#include <boost/multiprecision/cpp_double_fp.hpp>
#endif
namespace local {
template <class DataArrayType>
struct data_maker
{
static auto get_data() -> const DataArrayType& { static const DataArrayType instance { }; return instance; }
};
template <>
struct data_maker<test_pow_data::test_pow_data_array_type_reduced> final
{
using local_array_type = test_pow_data::test_pow_data_array_type_reduced;
static auto get_data() -> const local_array_type&
{
static const local_array_type instance { test_pow_data::data_reduced };
return instance;
}
};
template <>
struct data_maker<test_pow_data::test_pow_data_array_type_default> final
{
using local_array_type = test_pow_data::test_pow_data_array_type_default;
static auto get_data() -> const local_array_type&
{
static const local_array_type instance { test_pow_data::data };
return instance;
}
};
} // namespace local
template <class T>
void test()
{
std::cout << "Testing type: " << typeid(T).name() << std::endl;
using local_test_pow_data_array_type =
typename std::conditional<::has_poor_exp_range_or_precision_support<T>::value,
test_pow_data::test_pow_data_array_type_reduced,
test_pow_data::test_pow_data_array_type_default>::type;
const auto& data = local::data_maker<local_test_pow_data_array_type>::get_data();
unsigned max_err = 0;
for (unsigned k = 0; k < data.size(); k++)
{
T val = pow(T(data[k][0]), T(data[k][1]));
T e = relative_error(val, T(data[k][2]));
unsigned err = e.template convert_to<unsigned>();
if (err > max_err)
{
max_err = err;
}
val = pow(T(data[k][0]), -T(data[k][1]));
e = relative_error(val, T(1 / T(data[k][2])));
err = e.template convert_to<unsigned>();
if (err > max_err)
{
max_err = err;
}
}
std::cout << "Max error was: " << max_err << std::endl;
//
// Special cases come last:
//
BOOST_CHECK_EQUAL(pow(T(0), T(0)), 1);
BOOST_CHECK_EQUAL(pow(T(0), 0), 1);
BOOST_CHECK_EQUAL(pow(T(0), T(2)), 0);
BOOST_CHECK_EQUAL(pow(T(0), 2), 0);
BOOST_CHECK_EQUAL(pow(T(1), T(0)), 1);
BOOST_CHECK_EQUAL(pow(T(1), 0), 1);
BOOST_CHECK_EQUAL(pow(T(1), T(2)), 1);
BOOST_CHECK_EQUAL(pow(T(1), 2), 1);
BOOST_IF_CONSTEXPR(std::numeric_limits<T>::is_specialized && std::numeric_limits<T>::has_infinity)
{
BOOST_CHECK_EQUAL(pow(T(1.25F), std::numeric_limits<T>::infinity()), std::numeric_limits<T>::infinity());
BOOST_CHECK_EQUAL(pow(T(1.25F), -std::numeric_limits<T>::infinity()), T(0));
}
BOOST_IF_CONSTEXPR((!boost::multiprecision::is_interval_number<T>::value) && std::numeric_limits<T>::is_specialized && std::numeric_limits<T>::has_quiet_NaN)
{
BOOST_CHECK((boost::math::isnan)(pow(T(1.25F), std::numeric_limits<T>::quiet_NaN())));
BOOST_CHECK_EQUAL(pow(std::numeric_limits<T>::quiet_NaN(), T(0)), T(1));
BOOST_CHECK((boost::math::isnan)(pow(std::numeric_limits<T>::quiet_NaN(), T(1.25F))));
}
if (!boost::multiprecision::is_interval_number<T>::value)
{
T bug_case = -1.05 * log((std::numeric_limits<T>::max)()) / log(T(1.01));
for (unsigned i = 0; i < 100; ++i, bug_case *= 1.05)
{
if (std::numeric_limits<T>::has_infinity)
{
BOOST_CHECK_EQUAL(pow(T(1.01), bug_case), 0);
}
else
{
BOOST_CHECK_LE(pow(T(1.01), bug_case), (std::numeric_limits<T>::min)());
}
}
}
}
int main()
{
#ifdef TEST_BACKEND
test<boost::multiprecision::number<boost::multiprecision::concepts::number_backend_float_architype> >();
#endif
#ifdef TEST_MPF_50
test<boost::multiprecision::mpf_float_50>();
test<boost::multiprecision::mpf_float_100>();
#endif
#ifdef TEST_MPFR_50
test<boost::multiprecision::mpfr_float_50>();
test<boost::multiprecision::mpfr_float_100>();
#endif
#ifdef TEST_MPFI_50
test<boost::multiprecision::mpfi_float_50>();
test<boost::multiprecision::mpfi_float_100>();
#endif
#ifdef TEST_CPP_DEC_FLOAT
test<boost::multiprecision::cpp_dec_float_50>();
test<boost::multiprecision::cpp_dec_float_100>();
#ifndef SLOW_COMPLER
// Some "peculiar" digit counts which stress our code:
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<65> > >();
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<64> > >();
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<63> > >();
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<62> > >();
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<61, long long> > >();
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<60, long long> > >();
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<59, long long, std::allocator<char> > > >();
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<58, long long, std::allocator<char> > > >();
// Check low multiprecision digit counts.
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<9> > >();
test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<18> > >();
#endif
#endif
#ifdef TEST_FLOAT128
test<boost::multiprecision::float128>();
#endif
#ifdef TEST_CPP_BIN_FLOAT
test<boost::multiprecision::cpp_bin_float_50>();
test<boost::multiprecision::number<boost::multiprecision::cpp_bin_float<35, boost::multiprecision::digit_base_10, std::allocator<char>, long long> > >();
#endif
#ifdef TEST_CPP_DOUBLE_FLOAT
test<boost::multiprecision::cpp_double_double>();
test<boost::multiprecision::cpp_double_long_double>();
#if defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128)
test<boost::multiprecision::cpp_double_float128>();
#endif
#endif
return boost::report_errors();
}