1 // (C) Copyright John Maddock 2007.
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)
7 #define _SCL_SECURE_NO_WARNINGS
10 #include <boost/detail/lightweight_test.hpp>
11 #include <boost/math/special_functions/round.hpp>
12 #include <boost/math/special_functions/trunc.hpp>
13 #include <boost/math/special_functions/modf.hpp>
14 #include <boost/math/special_functions/sign.hpp>
15 #include <boost/random/mersenne_twister.hpp>
18 #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)
23 #define TEST_CPP_DEC_FLOAT
25 #define TEST_CPP_BIN_FLOAT
28 #pragma message("CAUTION!!: No backend type specified so testing everything.... this will take some time!!")
31 #pragma warning "CAUTION!!: No backend type specified so testing everything.... this will take some time!!"
36 #if defined(TEST_MPF_50)
37 #include <boost/multiprecision/gmp.hpp>
40 #include <boost/multiprecision/mpfr.hpp>
43 #include <boost/multiprecision/mpfi.hpp>
46 #include <boost/multiprecision/concepts/mp_number_archetypes.hpp>
48 #ifdef TEST_CPP_DEC_FLOAT
49 #include <boost/multiprecision/cpp_dec_float.hpp>
51 #ifdef TEST_CPP_BIN_FLOAT
52 #include <boost/multiprecision/cpp_bin_float.hpp>
55 #include <boost/multiprecision/float128.hpp>
59 #pragma warning(disable : 4127)
68 // Fill all the bits in T with random values,
69 // likewise set the exponent to a random value
70 // that will still fit inside a T, and always
71 // have a remainder as well as an integer part.
73 int bits = boost::math::tools::digits<T>();
75 int exponent = rng() % (bits - 4);
79 result += ldexp(static_cast<T>(rng()), shift);
80 shift += std::numeric_limits<int>::digits;
81 bits -= std::numeric_limits<int>::digits;
83 return rng() & 1u ? T(-ldexp(frexp(result, &bits), exponent)) : T(ldexp(frexp(result, &bits), exponent));
86 template <class T, class U>
87 typename boost::disable_if_c<boost::multiprecision::is_interval_number<T>::value>::type check_within_half(T a, U u)
90 if (fabs(a - u) > 0.5f)
92 BOOST_ERROR("Rounded result differed by more than 0.5 from the original");
93 std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
94 << std::left << a << u << std::endl;
96 if ((fabs(a - u) == 0.5f) && (fabs(static_cast<T>(u)) < fabs(a)))
98 BOOST_ERROR("Rounded result was towards zero with boost::round");
99 std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
100 << std::left << a << u << std::endl;
103 template <class T, class U>
104 typename boost::enable_if_c<boost::multiprecision::is_interval_number<T>::value>::type check_within_half(T a, U u)
107 if (upper(T(fabs(a - u))) > 0.5f)
109 BOOST_ERROR("Rounded result differed by more than 0.5 from the original");
110 std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
111 << std::left << a << u << std::endl;
113 if ((upper(T(fabs(a - u))) == 0.5f) && (fabs(static_cast<T>(u)) < fabs(a)))
115 BOOST_ERROR("Rounded result was towards zero with boost::round");
116 std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
117 << std::left << a << u << std::endl;
122 // We may not have an abs overload for long long so provide a fall back:
124 inline unsigned safe_abs(int const& v)
126 return v < 0 ? static_cast<unsigned>(1u) + static_cast<unsigned>(-(v + 1)) : v;
128 inline unsigned long safe_abs(long const& v)
130 return v < 0 ? static_cast<unsigned long>(1u) + static_cast<unsigned long>(-(v + 1)) : v;
132 inline unsigned long long safe_abs(long long const& v)
134 return v < 0 ? static_cast<unsigned long long>(1u) + static_cast<unsigned long long>(-(v + 1)) : v;
137 inline typename boost::disable_if_c<boost::is_integral<T>::value, T>::type safe_abs(T const& v)
139 return v < 0 ? -v : v;
142 template <class T, class U>
143 void check_trunc_result(T a, U u)
146 if (fabs(a - u) >= 1)
148 BOOST_ERROR("Rounded result differed by more than 1 from the original");
149 std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
150 << std::left << a << u << std::endl;
152 if (abs(a) < safe_abs(u))
154 BOOST_ERROR("Truncated result had larger absolute value than the original");
155 std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
156 << std::left << a << u << std::endl;
158 if (fabs(static_cast<T>(u)) > fabs(a))
160 BOOST_ERROR("Rounded result was away from zero with boost::trunc");
161 std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
162 << std::left << a << u << std::endl;
166 template <class T, class U>
167 void check_modf_result(T a, T fract, U ipart)
170 if (fract + ipart != a)
172 BOOST_ERROR("Fractional and integer results do not add up to the original value");
173 std::cerr << "Values were: " << std::setprecision(35) << " "
174 << std::left << a << ipart << " " << fract << std::endl;
176 if ((boost::math::sign(a) != boost::math::sign(fract)) && boost::math::sign(fract))
178 BOOST_ERROR("Original and fractional parts have differing signs");
179 std::cerr << "Values were: " << std::setprecision(35) << " "
180 << std::left << a << ipart << " " << fract << std::endl;
182 if ((boost::math::sign(a) != boost::math::sign(ipart)) && boost::math::sign(ipart))
184 BOOST_ERROR("Original and integer parts have differing signs");
185 std::cerr << "Values were: " << std::setprecision(35) << " "
186 << std::left << a << ipart << " " << ipart << std::endl;
188 if (fabs(a - ipart) >= 1)
190 BOOST_ERROR("Rounded result differed by more than 1 from the original");
191 std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
192 << std::left << a << ipart << std::endl;
201 for (int i = 0; i < 1000; ++i)
203 T arg = get_random<T>();
205 check_within_half(arg, r);
206 BOOST_TEST(r == round(arg + 0));
208 check_trunc_result(arg, r);
209 BOOST_TEST(r == trunc(arg + 0));
210 T frac = modf(arg, &r);
211 check_modf_result(arg, frac, r);
213 if (abs(r) < (std::numeric_limits<int>::max)())
216 check_within_half(arg, i);
217 BOOST_TEST(i == iround(arg + 0));
219 check_trunc_result(arg, i);
220 BOOST_TEST(i == itrunc(arg + 0));
222 check_modf_result(arg, r, i);
224 if (abs(r) < (std::numeric_limits<long>::max)())
226 long l = lround(arg);
227 check_within_half(arg, l);
228 BOOST_TEST(l == lround(arg + 0));
230 check_trunc_result(arg, l);
231 BOOST_TEST(l == ltrunc(arg + 0));
233 check_modf_result(arg, r, l);
236 #ifdef BOOST_HAS_LONG_LONG
237 if (abs(r) < (std::numeric_limits<boost::long_long_type>::max)())
239 boost::long_long_type ll = llround(arg);
240 check_within_half(arg, ll);
241 BOOST_TEST(ll == llround(arg + 0));
243 check_trunc_result(arg, ll);
244 BOOST_TEST(ll == lltrunc(arg + 0));
246 check_modf_result(arg, r, ll);
251 // Test boundary cases:
253 if (std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
255 int si = iround(static_cast<T>((std::numeric_limits<int>::max)()));
256 check_within_half(static_cast<T>((std::numeric_limits<int>::max)()), si);
257 BOOST_TEST(si == iround(static_cast<T>((std::numeric_limits<int>::max)()) + 0));
258 si = iround(static_cast<T>((std::numeric_limits<int>::min)()));
259 check_within_half(static_cast<T>((std::numeric_limits<int>::min)()), si);
260 BOOST_TEST(si == iround(static_cast<T>((std::numeric_limits<int>::min)()) + 0));
261 si = itrunc(static_cast<T>((std::numeric_limits<int>::max)()));
262 check_trunc_result(static_cast<T>((std::numeric_limits<int>::max)()), si);
263 BOOST_TEST(si == itrunc(static_cast<T>((std::numeric_limits<int>::max)()) + 0));
264 si = itrunc(static_cast<T>((std::numeric_limits<int>::min)()));
265 check_trunc_result(static_cast<T>((std::numeric_limits<int>::min)()), si);
266 BOOST_TEST(si == itrunc(static_cast<T>((std::numeric_limits<int>::min)()) + 0));
268 si = iround(static_cast<T>((std::numeric_limits<int>::max)() - 1));
269 check_within_half(static_cast<T>((std::numeric_limits<int>::max)() - 1), si);
270 si = iround(static_cast<T>((std::numeric_limits<int>::min)() + 1));
271 check_within_half(static_cast<T>((std::numeric_limits<int>::min)() + 1), si);
272 si = itrunc(static_cast<T>((std::numeric_limits<int>::max)() - 1));
273 check_trunc_result(static_cast<T>((std::numeric_limits<int>::max)() - 1), si);
274 si = itrunc(static_cast<T>((std::numeric_limits<int>::min)() + 1));
275 check_trunc_result(static_cast<T>((std::numeric_limits<int>::min)() + 1), si);
277 if (std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
279 long k = lround(static_cast<T>((std::numeric_limits<long>::max)()));
280 check_within_half(static_cast<T>((std::numeric_limits<long>::max)()), k);
281 BOOST_TEST(k == lround(static_cast<T>((std::numeric_limits<long>::max)()) + 0));
282 k = lround(static_cast<T>((std::numeric_limits<long>::min)()));
283 check_within_half(static_cast<T>((std::numeric_limits<long>::min)()), k);
284 BOOST_TEST(k == lround(static_cast<T>((std::numeric_limits<long>::min)()) + 0));
285 k = ltrunc(static_cast<T>((std::numeric_limits<long>::max)()));
286 check_trunc_result(static_cast<T>((std::numeric_limits<long>::max)()), k);
287 BOOST_TEST(k == ltrunc(static_cast<T>((std::numeric_limits<long>::max)()) + 0));
288 k = ltrunc(static_cast<T>((std::numeric_limits<long>::min)()));
289 check_trunc_result(static_cast<T>((std::numeric_limits<long>::min)()), k);
290 BOOST_TEST(k == ltrunc(static_cast<T>((std::numeric_limits<long>::min)()) + 0));
292 k = lround(static_cast<T>((std::numeric_limits<long>::max)() - 1));
293 check_within_half(static_cast<T>((std::numeric_limits<long>::max)() - 1), k);
294 k = lround(static_cast<T>((std::numeric_limits<long>::min)() + 1));
295 check_within_half(static_cast<T>((std::numeric_limits<long>::min)() + 1), k);
296 k = ltrunc(static_cast<T>((std::numeric_limits<long>::max)() - 1));
297 check_trunc_result(static_cast<T>((std::numeric_limits<long>::max)() - 1), k);
298 k = ltrunc(static_cast<T>((std::numeric_limits<long>::min)() + 1));
299 check_trunc_result(static_cast<T>((std::numeric_limits<long>::min)() + 1), k);
301 #ifndef BOOST_NO_LONG_LONG
302 if (std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
304 boost::long_long_type j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()));
305 check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()), j);
306 BOOST_TEST(j == llround(static_cast<T>((std::numeric_limits<long long>::max)()) + 0));
307 j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()));
308 check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()), j);
309 BOOST_TEST(j == llround(static_cast<T>((std::numeric_limits<long long>::min)()) + 0));
310 j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()));
311 check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()), j);
312 BOOST_TEST(j == lltrunc(static_cast<T>((std::numeric_limits<long long>::max)()) + 0));
313 j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()));
314 check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()), j);
315 BOOST_TEST(j == lltrunc(static_cast<T>((std::numeric_limits<long long>::min)()) + 0));
317 j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)() - 1));
318 check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)() - 1), j);
319 j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)() + 1));
320 check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)() + 1), j);
321 j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)() - 1));
322 check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)() - 1), j);
323 j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)() + 1));
324 check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)() + 1), j);
328 // Finish off by testing the error handlers:
331 #ifndef BOOST_NO_EXCEPTIONS
332 BOOST_CHECK_THROW(result = static_cast<T>(iround(static_cast<T>(1e20))), boost::math::rounding_error);
333 BOOST_CHECK_THROW(result = static_cast<T>(iround(static_cast<T>(-1e20))), boost::math::rounding_error);
334 BOOST_CHECK_THROW(result = static_cast<T>(lround(static_cast<T>(1e20))), boost::math::rounding_error);
335 BOOST_CHECK_THROW(result = static_cast<T>(lround(static_cast<T>(-1e20))), boost::math::rounding_error);
336 #ifdef BOOST_HAS_LONG_LONG
337 BOOST_CHECK_THROW(result = static_cast<T>(llround(static_cast<T>(1e20))), boost::math::rounding_error);
338 BOOST_CHECK_THROW(result = static_cast<T>(llround(static_cast<T>(-1e20))), boost::math::rounding_error);
340 if (std::numeric_limits<T>::has_infinity)
342 BOOST_CHECK_EQUAL(static_cast<T>(round(std::numeric_limits<T>::infinity())), std::numeric_limits<T>::infinity()); // See C99 Annex F.
343 BOOST_CHECK_THROW(result = static_cast<T>(iround(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
344 BOOST_CHECK_THROW(result = static_cast<T>(iround(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
345 BOOST_CHECK_THROW(result = static_cast<T>(lround(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
346 BOOST_CHECK_THROW(result = static_cast<T>(lround(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
347 #ifdef BOOST_HAS_LONG_LONG
348 BOOST_CHECK_THROW(result = static_cast<T>(llround(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
349 BOOST_CHECK_THROW(result = static_cast<T>(llround(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
352 if (std::numeric_limits<T>::has_quiet_NaN)
354 BOOST_CHECK((boost::multiprecision::isnan)(round(std::numeric_limits<T>::quiet_NaN())));
355 BOOST_CHECK_THROW(result = static_cast<T>(iround(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
356 BOOST_CHECK_THROW(result = static_cast<T>(lround(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
357 #ifdef BOOST_HAS_LONG_LONG
358 BOOST_CHECK_THROW(result = static_cast<T>(llround(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
361 BOOST_CHECK_THROW(result = static_cast<T>(itrunc(static_cast<T>(1e20))), boost::math::rounding_error);
362 BOOST_CHECK_THROW(result = static_cast<T>(itrunc(static_cast<T>(-1e20))), boost::math::rounding_error);
363 BOOST_CHECK_THROW(result = static_cast<T>(ltrunc(static_cast<T>(1e20))), boost::math::rounding_error);
364 BOOST_CHECK_THROW(result = static_cast<T>(ltrunc(static_cast<T>(-1e20))), boost::math::rounding_error);
365 #ifdef BOOST_HAS_LONG_LONG
366 BOOST_CHECK_THROW(result = static_cast<T>(lltrunc(static_cast<T>(1e20))), boost::math::rounding_error);
367 BOOST_CHECK_THROW(result = static_cast<T>(lltrunc(static_cast<T>(-1e20))), boost::math::rounding_error);
369 if (std::numeric_limits<T>::has_infinity)
371 BOOST_CHECK_EQUAL(static_cast<T>(trunc(std::numeric_limits<T>::infinity())), std::numeric_limits<T>::infinity());
372 BOOST_CHECK_EQUAL(static_cast<T>(trunc(-std::numeric_limits<T>::infinity())), -std::numeric_limits<T>::infinity());
373 BOOST_CHECK_THROW(result = static_cast<T>(itrunc(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
374 BOOST_CHECK_THROW(result = static_cast<T>(itrunc(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
375 BOOST_CHECK_THROW(result = static_cast<T>(ltrunc(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
376 BOOST_CHECK_THROW(result = static_cast<T>(ltrunc(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
377 #ifdef BOOST_HAS_LONG_LONG
378 BOOST_CHECK_THROW(result = static_cast<T>(lltrunc(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
379 BOOST_CHECK_THROW(result = static_cast<T>(lltrunc(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
382 if (std::numeric_limits<T>::has_quiet_NaN)
384 BOOST_CHECK((boost::multiprecision::isnan)(trunc(std::numeric_limits<T>::quiet_NaN())));
385 BOOST_CHECK_THROW(result = static_cast<T>(itrunc(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
386 BOOST_CHECK_THROW(result = static_cast<T>(ltrunc(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
387 #ifdef BOOST_HAS_LONG_LONG
388 BOOST_CHECK_THROW(result = static_cast<T>(lltrunc(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
391 if (std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
393 BOOST_CHECK_THROW(result = static_cast<T>(itrunc(static_cast<T>((std::numeric_limits<int>::max)()) + 1)), boost::math::rounding_error);
394 BOOST_CHECK_THROW(result = static_cast<T>(itrunc(static_cast<T>((std::numeric_limits<int>::min)()) - 1)), boost::math::rounding_error);
396 if (std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
398 BOOST_CHECK_THROW(result = static_cast<T>(ltrunc(static_cast<T>((std::numeric_limits<long>::max)()) + 1)), boost::math::rounding_error);
399 BOOST_CHECK_THROW(result = static_cast<T>(ltrunc(static_cast<T>((std::numeric_limits<long>::min)()) - 1)), boost::math::rounding_error);
401 #ifndef BOOST_NO_LONG_LONG
402 if (std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
404 BOOST_CHECK_THROW(result = static_cast<T>(lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()) + 1)), boost::math::rounding_error);
405 BOOST_CHECK_THROW(result = static_cast<T>(lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()) - 1)), boost::math::rounding_error);
408 if (std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
410 BOOST_CHECK_THROW(result = static_cast<T>(iround(static_cast<T>((std::numeric_limits<int>::max)()) + 1)), boost::math::rounding_error);
411 BOOST_CHECK_THROW(result = static_cast<T>(iround(static_cast<T>((std::numeric_limits<int>::min)()) - 1)), boost::math::rounding_error);
413 if (std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
415 BOOST_CHECK_THROW(result = static_cast<T>(lround(static_cast<T>((std::numeric_limits<long>::max)()) + 1)), boost::math::rounding_error);
416 BOOST_CHECK_THROW(result = static_cast<T>(lround(static_cast<T>((std::numeric_limits<long>::min)()) - 1)), boost::math::rounding_error);
418 #ifndef BOOST_NO_LONG_LONG
419 if (std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
421 BOOST_CHECK_THROW(result = static_cast<T>(llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()) + 1)), boost::math::rounding_error);
422 BOOST_CHECK_THROW(result = static_cast<T>(llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()) - 1)), boost::math::rounding_error);
431 test<boost::multiprecision::mpf_float_50>();
432 test<boost::multiprecision::mpf_float_100>();
435 test<boost::multiprecision::mpfr_float_50>();
436 test<boost::multiprecision::mpfr_float_100>();
439 test<boost::multiprecision::mpfi_float_50>();
440 test<boost::multiprecision::mpfi_float_100>();
442 #ifdef TEST_CPP_DEC_FLOAT
443 test<boost::multiprecision::cpp_dec_float_50>();
444 test<boost::multiprecision::cpp_dec_float_100>();
446 // Some "peculiar" digit counts which stress our code:
447 test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<65> > >();
448 test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<64> > >();
449 test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<63> > >();
450 test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<62> > >();
451 test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<61, long long> > >();
452 test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<60, long long> > >();
453 test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<59, long long, std::allocator<char> > > >();
454 test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<58, long long, std::allocator<char> > > >();
457 #ifdef TEST_CPP_BIN_FLOAT
458 test<boost::multiprecision::cpp_bin_float_50>();
459 test<boost::multiprecision::cpp_bin_float_100>();
460 test<boost::multiprecision::number<boost::multiprecision::cpp_bin_float<35, boost::multiprecision::digit_base_10, std::allocator<char>, boost::long_long_type> > >();
463 test<boost::multiprecision::number<boost::multiprecision::concepts::number_backend_float_architype> >();
466 test<boost::multiprecision::float128>();
468 return boost::report_errors();