File: reduced_cpp_double_fp.cpp

package info (click to toggle)
boost1.90 1.90.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 593,120 kB
  • sloc: cpp: 4,190,908; xml: 196,648; python: 34,618; ansic: 23,145; asm: 5,468; sh: 3,774; makefile: 1,161; perl: 1,020; sql: 728; ruby: 676; yacc: 478; java: 77; lisp: 24; csh: 6
file content (165 lines) | stat: -rw-r--r-- 6,325 bytes parent folder | download | duplicates (3)
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();
}