test_cpp_bin_float_conv.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. ///////////////////////////////////////////////////////////////
  2. // Copyright 2012 John Maddock. Distributed under the Boost
  3. // Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
  5. //
  6. #ifdef _MSC_VER
  7. #define _SCL_SECURE_NO_WARNINGS
  8. #endif
  9. #include <boost/detail/lightweight_test.hpp>
  10. #include <boost/array.hpp>
  11. #include "test.hpp"
  12. #include <boost/multiprecision/cpp_int.hpp>
  13. #include <boost/multiprecision/cpp_bin_float.hpp>
  14. #include <boost/random/mersenne_twister.hpp>
  15. #include <boost/random/uniform_int.hpp>
  16. #include <boost/cstdint.hpp>
  17. template <class T>
  18. T generate_random()
  19. {
  20. typedef int e_type;
  21. static boost::random::mt19937 gen;
  22. T val = gen();
  23. T prev_val = -1;
  24. while (val != prev_val)
  25. {
  26. val *= (gen.max)();
  27. prev_val = val;
  28. val += gen();
  29. }
  30. e_type e;
  31. val = frexp(val, &e);
  32. static boost::random::uniform_int_distribution<e_type> ui(-20, 20);
  33. return ldexp(val, ui(gen));
  34. }
  35. template <class T>
  36. void check_round(const T& val, bool check_extended = false)
  37. {
  38. double d1 = val.template convert_to<double>();
  39. double d2 = boost::math::nextafter(d1, d1 < val ? DBL_MAX : -DBL_MAX);
  40. T diff1 = abs(d1 - val);
  41. T diff2 = abs(d2 - val);
  42. if (diff2 < diff1)
  43. {
  44. // Some debugging code here...
  45. std::cout << val.str() << std::endl;
  46. std::cout << std::setprecision(18);
  47. std::cout << d1 << std::endl;
  48. std::cout << d2 << std::endl;
  49. std::cout << diff1 << std::endl;
  50. std::cout << diff2 << std::endl;
  51. d1 = val.template convert_to<double>();
  52. }
  53. BOOST_CHECK(diff2 >= diff1);
  54. BOOST_CHECK_EQUAL(boost::math::signbit(val), boost::math::signbit(d1));
  55. float f1 = val.template convert_to<float>();
  56. float f2 = boost::math::nextafter(f1, f1 < val ? FLT_MAX : -FLT_MAX);
  57. BOOST_CHECK(((abs(f1 - val) <= abs(f2 - val))));
  58. BOOST_CHECK_EQUAL(boost::math::signbit(val), boost::math::signbit(f1));
  59. #if !defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
  60. if (check_extended)
  61. {
  62. //
  63. // We should check long double as well:
  64. //
  65. long double l1 = val.template convert_to<long double>();
  66. long double l2 = boost::math::nextafter(d1, d1 < val ? LDBL_MAX : -LDBL_MAX);
  67. diff1 = abs(l1 - val);
  68. diff2 = abs(l2 - val);
  69. if (diff2 < diff1)
  70. {
  71. // Some debugging code here...
  72. std::cout << val.str() << std::endl;
  73. std::cout << std::setprecision(18);
  74. std::cout << l1 << std::endl;
  75. std::cout << l2 << std::endl;
  76. std::cout << diff1 << std::endl;
  77. std::cout << diff2 << std::endl;
  78. l1 = val.template convert_to<long double>();
  79. }
  80. BOOST_CHECK(diff2 >= diff1);
  81. BOOST_CHECK_EQUAL(boost::math::signbit(val), boost::math::signbit(l1));
  82. }
  83. #endif
  84. }
  85. int main()
  86. {
  87. using namespace boost::multiprecision;
  88. cpp_int i(20);
  89. cpp_bin_float_50 f50(i);
  90. BOOST_CHECK_EQUAL(f50, 20);
  91. f50 = i.convert_to<cpp_bin_float_50>();
  92. BOOST_CHECK_EQUAL(f50, 20);
  93. int1024_t i1024(45);
  94. cpp_bin_float_100 f100(i1024);
  95. BOOST_CHECK_EQUAL(f100, 45);
  96. f100 = i1024.convert_to<cpp_bin_float_100>();
  97. BOOST_CHECK_EQUAL(f100, 45);
  98. uint1024_t ui1024(55);
  99. cpp_bin_float_100 f100b(ui1024);
  100. BOOST_CHECK_EQUAL(f100b, 55);
  101. f100b = ui1024.convert_to<cpp_bin_float_100>();
  102. BOOST_CHECK_EQUAL(f100b, 55);
  103. typedef number<cpp_int_backend<32, 32>, et_off> i32_t;
  104. i32_t i32(67);
  105. cpp_bin_float_100 f100c(i32);
  106. BOOST_CHECK_EQUAL(f100c, 67);
  107. f100c = i32.convert_to<cpp_bin_float_100>();
  108. BOOST_CHECK_EQUAL(f100c, 67);
  109. typedef number<cpp_int_backend<32, 32, unsigned_magnitude>, et_off> ui32_t;
  110. ui32_t ui32(98);
  111. cpp_bin_float_100 f100d(ui32);
  112. BOOST_CHECK_EQUAL(f100d, 98);
  113. f100d = ui32.convert_to<cpp_bin_float_100>();
  114. BOOST_CHECK_EQUAL(f100d, 98);
  115. typedef number<cpp_int_backend<64, 64>, et_off> i64_t;
  116. i64_t i64(67);
  117. cpp_bin_float_100 f100e(i64);
  118. BOOST_CHECK_EQUAL(f100e, 67);
  119. f100e = i64.convert_to<cpp_bin_float_100>();
  120. BOOST_CHECK_EQUAL(f100e, 67);
  121. typedef number<cpp_int_backend<64, 64, unsigned_magnitude>, et_off> ui64_t;
  122. ui64_t ui64(98);
  123. cpp_bin_float_100 f100f(ui64);
  124. BOOST_CHECK_EQUAL(f100f, 98);
  125. f100f = ui64.convert_to<cpp_bin_float_100>();
  126. BOOST_CHECK_EQUAL(f100f, 98);
  127. typedef number<cpp_int_backend<128, 128>, et_off> i128_t;
  128. i128_t i128(67);
  129. cpp_bin_float_100 f100g(i128);
  130. BOOST_CHECK_EQUAL(f100g, 67);
  131. f100g = i128.convert_to<cpp_bin_float_100>();
  132. BOOST_CHECK_EQUAL(f100g, 67);
  133. typedef number<cpp_int_backend<128, 128, unsigned_magnitude>, et_off> ui128_t;
  134. ui128_t ui128(98);
  135. cpp_bin_float_100 f100h(ui128);
  136. BOOST_CHECK_EQUAL(f100h, 98);
  137. f100h = ui128.convert_to<cpp_bin_float_100>();
  138. BOOST_CHECK_EQUAL(f100h, 98);
  139. cpp_bin_float_quad q = (std::numeric_limits<float>::min)();
  140. BOOST_CHECK_EQUAL(q.convert_to<float>(), (std::numeric_limits<float>::min)());
  141. q = (std::numeric_limits<float>::max)();
  142. BOOST_CHECK_EQUAL(q.convert_to<float>(), (std::numeric_limits<float>::max)());
  143. q = (std::numeric_limits<float>::denorm_min)();
  144. BOOST_CHECK_EQUAL(q.convert_to<float>(), (std::numeric_limits<float>::denorm_min)());
  145. // See https://svn.boost.org/trac/boost/ticket/12512:
  146. boost::multiprecision::number<boost::multiprecision::backends::cpp_bin_float<128, boost::multiprecision::backends::digit_base_2, void, boost::int64_t> > ext_float("1e-646456978");
  147. BOOST_CHECK_EQUAL(ext_float.convert_to<float>(), 0);
  148. q = -(std::numeric_limits<float>::min)();
  149. BOOST_CHECK_EQUAL(q.convert_to<float>(), -(std::numeric_limits<float>::min)());
  150. q = -(std::numeric_limits<float>::max)();
  151. BOOST_CHECK_EQUAL(q.convert_to<float>(), -(std::numeric_limits<float>::max)());
  152. q = -(std::numeric_limits<float>::denorm_min)();
  153. BOOST_CHECK_EQUAL(q.convert_to<float>(), -(std::numeric_limits<float>::denorm_min)());
  154. // See https://svn.boost.org/trac/boost/ticket/12512:
  155. ext_float = boost::multiprecision::number<boost::multiprecision::backends::cpp_bin_float<128, boost::multiprecision::backends::digit_base_2, void, boost::int64_t> >("-1e-646456978");
  156. BOOST_CHECK_EQUAL(ext_float.convert_to<float>(), 0);
  157. ext_float = (std::numeric_limits<double>::max)();
  158. ext_float *= 16;
  159. if (std::numeric_limits<double>::has_infinity)
  160. {
  161. BOOST_CHECK_EQUAL(ext_float.convert_to<double>(), std::numeric_limits<double>::infinity());
  162. }
  163. else
  164. {
  165. BOOST_CHECK_EQUAL(ext_float.convert_to<double>(), (std::numeric_limits<double>::max)());
  166. }
  167. ext_float = -ext_float;
  168. if (std::numeric_limits<double>::has_infinity)
  169. {
  170. BOOST_CHECK_EQUAL(ext_float.convert_to<double>(), -std::numeric_limits<double>::infinity());
  171. }
  172. else
  173. {
  174. BOOST_CHECK_EQUAL(ext_float.convert_to<double>(), -(std::numeric_limits<double>::max)());
  175. }
  176. //
  177. // Check for double rounding when the result would be a denorm.
  178. // See https://svn.boost.org/trac/boost/ticket/12527
  179. //
  180. cpp_bin_float_50 r1 = ldexp(cpp_bin_float_50(0x8000000000000bffull), -63 - 1023);
  181. check_round(r1);
  182. r1 = -r1;
  183. check_round(r1);
  184. r1 = ldexp(cpp_bin_float_50(0x8000017f), -31 - 127);
  185. check_round(r1);
  186. r1 = -r1;
  187. check_round(r1);
  188. //
  189. // Check convertion to signed zero works OK:
  190. //
  191. r1 = -ldexp(cpp_bin_float_50(1), -3000);
  192. check_round(r1);
  193. r1 = 0;
  194. r1 = -r1;
  195. BOOST_CHECK(boost::math::signbit(r1));
  196. check_round(r1);
  197. //
  198. // Check boundary as the exponent drops below what a double can cope with
  199. // but we will be rounding up:
  200. //
  201. r1 = 3;
  202. r1 = ldexp(r1, std::numeric_limits<double>::min_exponent);
  203. do
  204. {
  205. check_round(r1);
  206. check_round(boost::math::float_next(r1));
  207. check_round(boost::math::float_prior(r1));
  208. r1 = ldexp(r1, -1);
  209. } while (ilogb(r1) > std::numeric_limits<double>::min_exponent - 5 - std::numeric_limits<double>::digits);
  210. r1 = -3;
  211. r1 = ldexp(r1, std::numeric_limits<double>::min_exponent);
  212. do
  213. {
  214. check_round(r1);
  215. check_round(boost::math::float_next(r1));
  216. check_round(boost::math::float_prior(r1));
  217. r1 = ldexp(r1, -1);
  218. } while (ilogb(r1) > std::numeric_limits<double>::min_exponent - 5 - std::numeric_limits<double>::digits);
  219. //
  220. // Again when not rounding up:
  221. //
  222. r1 = 1;
  223. r1 = ldexp(r1, std::numeric_limits<double>::min_exponent);
  224. do
  225. {
  226. check_round(r1);
  227. check_round(boost::math::float_next(r1));
  228. check_round(boost::math::float_prior(r1));
  229. r1 = ldexp(r1, -1);
  230. } while (ilogb(r1) > std::numeric_limits<double>::min_exponent - 5 - std::numeric_limits<double>::digits);
  231. r1 = -1;
  232. r1 = ldexp(r1, std::numeric_limits<double>::min_exponent);
  233. do
  234. {
  235. check_round(r1);
  236. check_round(boost::math::float_next(r1));
  237. check_round(boost::math::float_prior(r1));
  238. r1 = ldexp(r1, -1);
  239. } while (ilogb(r1) > std::numeric_limits<double>::min_exponent - 5 - std::numeric_limits<double>::digits);
  240. //
  241. // Test random conversions:
  242. //
  243. for (unsigned j = 0; j < 10000; ++j)
  244. {
  245. check_round(generate_random<cpp_bin_float_50>(), true);
  246. }
  247. return boost::report_errors();
  248. }