1 // (C) Copyright John Maddock 2008.
2 // Use, modification and distribution are subject to the
3 // Boost Software License, Version 1.0. (See accompanying file
4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5
6 #include <pch.hpp>
7
8 #include <boost/math/concepts/real_concept.hpp>
9 #include <boost/math/tools/test.hpp>
10 #define BOOST_TEST_MAIN
11 #include <boost/test/unit_test.hpp>
12 #include <boost/test/tools/floating_point_comparison.hpp>
13 #include <boost/math/special_functions/next.hpp>
14 #include <boost/math/special_functions/ulp.hpp>
15 #include <boost/multiprecision/cpp_dec_float.hpp>
16 #include <boost/multiprecision/debug_adaptor.hpp>
17 #include <iostream>
18 #include <iomanip>
19
20 #ifdef BOOST_MSVC
21 #pragma warning(disable:4127)
22 #endif
23
24 template <class T>
is_normalized_value(const T & val)25 bool is_normalized_value(const T& val)
26 {
27 //
28 // Returns false if value has guard digits that are non-zero
29 //
30 boost::intmax_t shift = std::numeric_limits<T>::digits - ilogb(val) - 1;
31 T shifted = scalbn(val, shift);
32 return floor(shifted) == shifted;
33 }
34
35 template <class T>
test_value(const T & val,const char * name)36 void test_value(const T& val, const char* name)
37 {
38 using namespace boost::math;
39 T upper = tools::max_value<T>();
40 T lower = -upper;
41
42 std::cout << "Testing type " << name << " with initial value " << val << std::endl;
43
44 BOOST_CHECK_EQUAL(float_distance(float_next(val), val), -1);
45 BOOST_CHECK(float_next(val) > val);
46 BOOST_CHECK_EQUAL(float_distance(float_prior(val), val), 1);
47 BOOST_CHECK(float_prior(val) < val);
48 BOOST_CHECK_EQUAL(float_distance((boost::math::nextafter)(val, upper), val), -1);
49 BOOST_CHECK((boost::math::nextafter)(val, upper) > val);
50 BOOST_CHECK_EQUAL(float_distance((boost::math::nextafter)(val, lower), val), 1);
51 BOOST_CHECK((boost::math::nextafter)(val, lower) < val);
52 BOOST_CHECK_EQUAL(float_distance(float_next(float_next(val)), val), -2);
53 BOOST_CHECK_EQUAL(float_distance(float_prior(float_prior(val)), val), 2);
54 BOOST_CHECK_EQUAL(float_distance(float_prior(float_prior(val)), float_next(float_next(val))), 4);
55 BOOST_CHECK_EQUAL(float_distance(float_prior(float_next(val)), val), 0);
56 BOOST_CHECK_EQUAL(float_distance(float_next(float_prior(val)), val), 0);
57 if (is_normalized_value(val))
58 {
59 BOOST_CHECK_EQUAL(float_prior(float_next(val)), val);
60 BOOST_CHECK_EQUAL(float_next(float_prior(val)), val);
61 }
62 BOOST_CHECK_EQUAL(float_distance(float_advance(val, 4), val), -4);
63 BOOST_CHECK_EQUAL(float_distance(float_advance(val, -4), val), 4);
64 if(std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present))
65 {
66 BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), 4), float_next(float_next(val))), -4);
67 BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), -4), float_next(float_next(val))), 4);
68 }
69 if (is_normalized_value(val))
70 {
71 if (val > 0)
72 {
73 T n = val + ulp(val);
74 T fn = float_next(val);
75 if (n > fn)
76 {
77 BOOST_CHECK_LE(ulp(val), boost::math::tools::min_value<T>());
78 }
79 else
80 {
81 BOOST_CHECK_EQUAL(fn, n);
82 }
83 }
84 else if (val == 0)
85 {
86 BOOST_CHECK_GE(boost::math::tools::min_value<T>(), ulp(val));
87 }
88 else
89 {
90 T n = val - ulp(val);
91 T fp = float_prior(val);
92 if (n < fp)
93 {
94 BOOST_CHECK_LE(ulp(val), boost::math::tools::min_value<T>());
95 }
96 else
97 {
98 BOOST_CHECK_EQUAL(fp, n);
99 }
100 }
101 }
102 }
103
104 template <class T>
test_values(const T & val,const char * name)105 void test_values(const T& val, const char* name)
106 {
107 static const T a = boost::lexical_cast<T>("1.3456724e22");
108 static const T b = boost::lexical_cast<T>("1.3456724e-22");
109 static const T z = 0;
110 static const T one = 1;
111 static const T radix = std::numeric_limits<T>::radix;
112
113 std::cout << "Testing type " << name << std::endl;
114
115 T den = (std::numeric_limits<T>::min)() / 4;
116 if(den != 0)
117 {
118 std::cout << "Denormals are active\n";
119 }
120 else
121 {
122 std::cout << "Denormals are flushed to zero.\n";
123 }
124
125 test_value(a, name);
126 test_value(T(-a), name);
127 test_value(b, name);
128 test_value(T(-b), name);
129 test_value(T(b / 3), name);
130 test_value(T(-b / 3), name);
131 test_value(boost::math::tools::epsilon<T>(), name);
132 test_value(T(-boost::math::tools::epsilon<T>()), name);
133 test_value(boost::math::tools::min_value<T>(), name);
134 test_value(T(-boost::math::tools::min_value<T>()), name);
135 if (std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present) && ((std::numeric_limits<T>::min)() / 2 != 0))
136 {
137 test_value(z, name);
138 test_value(T(-z), name);
139 }
140 test_value(one, name);
141 test_value(T(-one), name);
142 test_value(radix, name);
143 test_value(T(-radix), name);
144
145 if(std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present) && ((std::numeric_limits<T>::min)() / 2 != 0))
146 {
147 test_value(std::numeric_limits<T>::denorm_min(), name);
148 test_value(T(-std::numeric_limits<T>::denorm_min()), name);
149 test_value(T(2 * std::numeric_limits<T>::denorm_min()), name);
150 test_value(T(-2 * std::numeric_limits<T>::denorm_min()), name);
151 }
152
153 static const int primes[] = {
154 11, 13, 17, 19, 23, 29,
155 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
156 73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
157 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
158 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
159 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
160 283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
161 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
162 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
163 };
164
165 for(unsigned i = 0; i < sizeof(primes)/sizeof(primes[0]); ++i)
166 {
167 T v1 = val;
168 T v2 = val;
169 for(int j = 0; j < primes[i]; ++j)
170 {
171 v1 = boost::math::float_next(v1);
172 v2 = boost::math::float_prior(v2);
173 }
174 BOOST_CHECK_EQUAL(boost::math::float_distance(v1, val), -primes[i]);
175 BOOST_CHECK_EQUAL(boost::math::float_distance(v2, val), primes[i]);
176 BOOST_CHECK_EQUAL(boost::math::float_advance(val, primes[i]), v1);
177 BOOST_CHECK_EQUAL(boost::math::float_advance(val, -primes[i]), v2);
178 }
179 if(std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_infinity))
180 {
181 BOOST_CHECK_EQUAL(boost::math::float_prior(std::numeric_limits<T>::infinity()), (std::numeric_limits<T>::max)());
182 BOOST_CHECK_EQUAL(boost::math::float_next(-std::numeric_limits<T>::infinity()), -(std::numeric_limits<T>::max)());
183 BOOST_MATH_CHECK_THROW(boost::math::float_prior(-std::numeric_limits<T>::infinity()), std::domain_error);
184 BOOST_MATH_CHECK_THROW(boost::math::float_next(std::numeric_limits<T>::infinity()), std::domain_error);
185 if(boost::math::policies:: BOOST_MATH_OVERFLOW_ERROR_POLICY == boost::math::policies::throw_on_error)
186 {
187 BOOST_MATH_CHECK_THROW(boost::math::float_prior(-(std::numeric_limits<T>::max)()), std::overflow_error);
188 BOOST_MATH_CHECK_THROW(boost::math::float_next((std::numeric_limits<T>::max)()), std::overflow_error);
189 }
190 else
191 {
192 BOOST_CHECK_EQUAL(boost::math::float_prior(-(std::numeric_limits<T>::max)()), -std::numeric_limits<T>::infinity());
193 BOOST_CHECK_EQUAL(boost::math::float_next((std::numeric_limits<T>::max)()), std::numeric_limits<T>::infinity());
194 }
195 }
196 }
197
BOOST_AUTO_TEST_CASE(test_main)198 BOOST_AUTO_TEST_CASE( test_main )
199 {
200 // Very slow, but debuggable:
201 //test_values(boost::multiprecision::number<boost::multiprecision::debug_adaptor<boost::multiprecision::cpp_dec_float_50::backend_type> >(0), "cpp_dec_float_50");
202
203 // Faster, but no good for diagnosing the cause of any issues:
204 test_values(boost::multiprecision::cpp_dec_float_50(0), "cpp_dec_float_50");
205 }
206
207
208