1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
// Copyright John Maddock 2007.
// Copyright Matt Borland 2023.
// 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 <boost/core/lightweight_test.hpp>
#include <boost/multiprecision/cpp_double_fp.hpp>
#include <boost/math/special_functions/modf.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/math/special_functions/sign.hpp>
#include <boost/math/special_functions/trunc.hpp>
#include <iomanip>
#include <iostream>
#include <limits>
#include <random>
#include <type_traits>
template <class T, class U>
typename std::enable_if<!boost::multiprecision::is_interval_number<T>::value>::type check_within_half(T a, U u)
{
BOOST_MATH_STD_USING
const auto fabs_res {fabs(a - u)};
if (fabs_res > 0.5f)
{
// LCOV_EXCL_START
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;
// LCOV_EXCL_STOP
}
if ((fabs(a - u) == 0.5f) && (fabs(static_cast<T>(u)) < fabs(a)))
{
// LCOV_EXCL_START
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;
// LCOV_EXCL_STOP
}
}
template <class T, class U>
typename std::enable_if<boost::multiprecision::is_interval_number<T>::value>::type check_within_half(T a, U u)
{
BOOST_MATH_STD_USING
if (upper(T(fabs(a - u))) > 0.5f)
{
// LCOV_EXCL_START
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;
// LCOV_EXCL_STOP
}
if ((upper(T(fabs(a - u))) == 0.5f) && (fabs(static_cast<T>(u)) < fabs(a)))
{
// LCOV_EXCL_START
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;
// LCOV_EXCL_STOP
}
}
//
// 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<unsigned>(1u) + static_cast<unsigned>(-(v + 1)) : v;
}
inline unsigned long safe_abs(long const& v)
{
return v < 0 ? static_cast<unsigned long>(1u) + static_cast<unsigned long>(-(v + 1)) : v;
}
inline unsigned long long safe_abs(long long const& v)
{
return v < 0 ? static_cast<unsigned long long>(1u) + static_cast<unsigned long long>(-(v + 1)) : v;
}
template <class T>
inline typename std::enable_if<!boost::multiprecision::detail::is_integral<T>::value, T>::type safe_abs(T const& v)
{
return v < 0 ? -v : v;
}
template <class T, class U>
void check_trunc_result(T a, U u)
{
BOOST_MATH_STD_USING
if (fabs(a - u) >= 1)
{
// LCOV_EXCL_START
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;
// LCOV_EXCL_STOP
}
if (abs(a) < safe_abs(u))
{
// LCOV_EXCL_START
BOOST_ERROR("Truncated result had larger absolute value than the original");
std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
<< std::left << abs(a) << safe_abs(u) << std::endl;
// LCOV_EXCL_STOP
}
if (fabs(static_cast<T>(u)) > fabs(a))
{
// LCOV_EXCL_START
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;
// LCOV_EXCL_STOP
}
}
template <typename T>
void test()
{
if (std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
{
auto lhs = static_cast<T>((std::numeric_limits<long>::max)());
long k = lround(lhs);
check_within_half(lhs, k);
BOOST_TEST(k == lround(static_cast<T>((std::numeric_limits<long>::max)()) + 0));
k = lround(static_cast<T>((std::numeric_limits<long>::min)()));
check_within_half(static_cast<T>((std::numeric_limits<long>::min)()), k);
BOOST_TEST(k == lround(static_cast<T>((std::numeric_limits<long>::min)()) + 0));
k = ltrunc(static_cast<T>((std::numeric_limits<long>::max)()));
check_trunc_result(static_cast<T>((std::numeric_limits<long>::max)()), k);
BOOST_TEST(k == ltrunc(static_cast<T>((std::numeric_limits<long>::max)()) + 0));
k = ltrunc(static_cast<T>((std::numeric_limits<long>::min)()));
check_trunc_result(static_cast<T>((std::numeric_limits<long>::min)()), k);
BOOST_TEST(k == ltrunc(static_cast<T>((std::numeric_limits<long>::min)()) + 0));
lhs = static_cast<T>((std::numeric_limits<long>::max)() - 1);
k = lround(lhs);
check_within_half(lhs, k);
k = lround(static_cast<T>((std::numeric_limits<long>::min)() + 1));
check_within_half(static_cast<T>((std::numeric_limits<long>::min)() + 1), k);
k = ltrunc(static_cast<T>((std::numeric_limits<long>::max)() - 1));
check_trunc_result(static_cast<T>((std::numeric_limits<long>::max)() - 1), k);
k = ltrunc(static_cast<T>((std::numeric_limits<long>::min)() + 1));
check_trunc_result(static_cast<T>((std::numeric_limits<long>::min)() + 1), k);
// Further testing of the boundary since the upper end of long is not exactly representable by double
for (long i = 2; i < 256; ++i)
{
lhs = static_cast<T>((std::numeric_limits<long>::max)() - i);
k = lround(lhs);
check_within_half(lhs, k);
}
}
}
int main()
{
test<boost::multiprecision::cpp_double_float>();
test<boost::multiprecision::cpp_double_double>();
// Older PPC64LE have ibm128 instead of ieee128 which is not compatible with this type
#if !defined(__PPC__)
test<boost::multiprecision::cpp_double_long_double>();
#endif
return boost::report_errors();
}
|