]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Copyright John Maddock 2013. |
2 | ||
3 | // Use, modification and distribution are subject to the | |
4 | // Boost Software License, Version 1.0. | |
5 | // (See accompanying file LICENSE_1_0.txt | |
6 | // or copy at http://www.boost.org/LICENSE_1_0.txt) | |
7 | ||
8 | #ifdef _MSC_VER | |
92f5a8d4 | 9 | #define _SCL_SECURE_NO_WARNINGS |
7c673cae FG |
10 | #endif |
11 | ||
12 | #if defined(TEST1) || defined(TEST2) || defined(TEST3) || defined(TEST4) | |
13 | #include <boost/multiprecision/cpp_bin_float.hpp> | |
14 | #include <boost/multiprecision/cpp_int.hpp> | |
15 | #else | |
16 | #include <boost/multiprecision/mpfr.hpp> | |
17 | #endif | |
18 | #include <boost/math/special_functions/next.hpp> | |
19 | ||
20 | #include <boost/random/mersenne_twister.hpp> | |
21 | #include <boost/random/uniform_int.hpp> | |
22 | #include <boost/chrono.hpp> | |
23 | #include "test.hpp" | |
24 | #include <boost/array.hpp> | |
25 | #include <iostream> | |
26 | #include <iomanip> | |
27 | ||
28 | #ifdef BOOST_MSVC | |
92f5a8d4 | 29 | #pragma warning(disable : 4127) |
7c673cae FG |
30 | #endif |
31 | ||
32 | template <class Clock> | |
33 | struct stopwatch | |
34 | { | |
35 | typedef typename Clock::duration duration; | |
36 | stopwatch() | |
37 | { | |
38 | m_start = Clock::now(); | |
39 | } | |
40 | duration elapsed() | |
41 | { | |
42 | return Clock::now() - m_start; | |
43 | } | |
44 | void reset() | |
45 | { | |
46 | m_start = Clock::now(); | |
47 | } | |
48 | ||
92f5a8d4 | 49 | private: |
7c673cae FG |
50 | typename Clock::time_point m_start; |
51 | }; | |
52 | ||
53 | template <class T> | |
92f5a8d4 | 54 | struct exponent_type |
7c673cae FG |
55 | { |
56 | typedef int type; | |
57 | }; | |
58 | template <class T, boost::multiprecision::expression_template_option ET> | |
59 | struct exponent_type<boost::multiprecision::number<T, ET> > | |
60 | { | |
61 | typedef typename T::exponent_type type; | |
62 | }; | |
63 | ||
64 | template <class T> | |
65 | T generate_random_float() | |
66 | { | |
67 | BOOST_MATH_STD_USING | |
68 | typedef typename exponent_type<T>::type e_type; | |
92f5a8d4 TL |
69 | static boost::random::mt19937 gen; |
70 | T val = gen(); | |
71 | T prev_val = -1; | |
72 | while (val != prev_val) | |
7c673cae FG |
73 | { |
74 | val *= (gen.max)(); | |
75 | prev_val = val; | |
76 | val += gen(); | |
77 | } | |
78 | e_type e; | |
79 | val = frexp(val, &e); | |
80 | ||
92f5a8d4 | 81 | static const int max_exponent_value = (std::min)(static_cast<int>(std::numeric_limits<T>::max_exponent - std::numeric_limits<T>::digits - 20), 2000); |
7c673cae FG |
82 | static boost::random::uniform_int_distribution<e_type> ui(0, max_exponent_value); |
83 | return ldexp(val, ui(gen)); | |
84 | } | |
85 | ||
86 | template <class Float, class Rat> | |
87 | void do_round_trip(const Float& val) | |
88 | { | |
7c673cae | 89 | BOOST_MATH_STD_USING |
92f5a8d4 | 90 | Rat rat(val); |
7c673cae FG |
91 | Float new_f(rat); |
92 | BOOST_CHECK_EQUAL(val, new_f); | |
93 | // | |
94 | // Try adding or subtracting an insignificant amount | |
95 | // (0.25ulp) from rat and check that it rounds to the same value: | |
96 | // | |
97 | typename exponent_type<Float>::type e; | |
92f5a8d4 | 98 | Float t = frexp(val, &e); |
7c673cae FG |
99 | (void)t; // warning suppression |
100 | e -= std::numeric_limits<Float>::digits + 2; | |
1e59de90 | 101 | BOOST_MP_ASSERT(val == (val + ldexp(Float(1), e))); |
92f5a8d4 | 102 | Rat delta, rounded; |
7c673cae | 103 | typedef typename boost::multiprecision::component_type<Rat>::type i_type; |
92f5a8d4 | 104 | i_type i(1); |
7c673cae | 105 | i <<= (e < 0 ? -e : e); |
92f5a8d4 | 106 | if (e > 0) |
7c673cae FG |
107 | delta.assign(i); |
108 | else | |
109 | delta = Rat(i_type(1), i); | |
110 | rounded = rat + delta; | |
92f5a8d4 | 111 | new_f = static_cast<Float>(rounded); |
7c673cae FG |
112 | BOOST_CHECK_EQUAL(val, new_f); |
113 | rounded = rat - delta; | |
92f5a8d4 | 114 | new_f = static_cast<Float>(rounded); |
7c673cae FG |
115 | BOOST_CHECK_EQUAL(val, new_f); |
116 | ||
117 | delta /= 2; | |
118 | rounded = rat + delta; | |
92f5a8d4 | 119 | new_f = static_cast<Float>(rounded); |
7c673cae FG |
120 | BOOST_CHECK_EQUAL(val, new_f); |
121 | rounded = rat - delta; | |
92f5a8d4 | 122 | new_f = static_cast<Float>(rounded); |
7c673cae FG |
123 | BOOST_CHECK_EQUAL(val, new_f); |
124 | ||
125 | delta /= 2; | |
126 | rounded = rat + delta; | |
92f5a8d4 | 127 | new_f = static_cast<Float>(rounded); |
7c673cae FG |
128 | BOOST_CHECK_EQUAL(val, new_f); |
129 | rounded = rat - delta; | |
92f5a8d4 | 130 | new_f = static_cast<Float>(rounded); |
7c673cae | 131 | BOOST_CHECK_EQUAL(val, new_f); |
7c673cae FG |
132 | } |
133 | ||
134 | template <class Float, class Rat> | |
135 | void test_round_trip() | |
136 | { | |
7c673cae FG |
137 | std::cout << "Testing types " << typeid(Float).name() << " <<==>> " << typeid(Rat).name() << std::endl; |
138 | std::cout << "digits = " << std::numeric_limits<Float>::digits << std::endl; | |
139 | std::cout << "digits10 = " << std::numeric_limits<Float>::digits10 << std::endl; | |
7c673cae | 140 | std::cout << "max_digits10 = " << std::numeric_limits<Float>::max_digits10 << std::endl; |
7c673cae FG |
141 | |
142 | stopwatch<boost::chrono::high_resolution_clock> w; | |
143 | ||
144 | int count = 0; | |
145 | ||
b32b8144 FG |
146 | #ifndef CI_SUPPRESS_KNOWN_ISSUES |
147 | while (boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() < 200) | |
148 | #else | |
149 | while (boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() < 50) | |
150 | #endif | |
7c673cae FG |
151 | { |
152 | Float val = generate_random_float<Float>(); | |
153 | do_round_trip<Float, Rat>(val); | |
154 | do_round_trip<Float, Rat>(Float(-val)); | |
92f5a8d4 TL |
155 | do_round_trip<Float, Rat>(Float(1 / val)); |
156 | do_round_trip<Float, Rat>(Float(-1 / val)); | |
7c673cae | 157 | count += 4; |
92f5a8d4 | 158 | if (boost::detail::test_errors() > 100) |
7c673cae FG |
159 | break; |
160 | } | |
161 | ||
162 | std::cout << "Execution time = " << boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() << "s" << std::endl; | |
163 | std::cout << "Total values tested: " << count << std::endl; | |
7c673cae FG |
164 | } |
165 | ||
166 | template <class Int> | |
167 | Int generate_random_int() | |
168 | { | |
92f5a8d4 | 169 | static boost::random::mt19937 gen; |
7c673cae FG |
170 | static boost::random::uniform_int_distribution<boost::random::mt19937::result_type> d(1, 20); |
171 | ||
172 | int lim; | |
173 | Int cppi(0); | |
174 | ||
175 | lim = d(gen); | |
176 | ||
92f5a8d4 | 177 | for (int i = 0; i < lim; ++i) |
7c673cae FG |
178 | { |
179 | cppi *= (gen.max)(); | |
180 | cppi += gen(); | |
181 | } | |
182 | return cppi; | |
183 | } | |
184 | ||
185 | template <class Float, class Rat> | |
186 | void test_random_rationals() | |
187 | { | |
7c673cae FG |
188 | std::cout << "Testing types " << typeid(Float).name() << " <<==>> " << typeid(Rat).name() << std::endl; |
189 | std::cout << "digits = " << std::numeric_limits<Float>::digits << std::endl; | |
190 | std::cout << "digits10 = " << std::numeric_limits<Float>::digits10 << std::endl; | |
7c673cae | 191 | std::cout << "max_digits10 = " << std::numeric_limits<Float>::max_digits10 << std::endl; |
7c673cae FG |
192 | |
193 | typedef typename boost::multiprecision::component_type<Rat>::type i_type; | |
92f5a8d4 | 194 | stopwatch<boost::chrono::high_resolution_clock> w; |
7c673cae FG |
195 | |
196 | int count = 0; | |
197 | ||
b32b8144 FG |
198 | #ifndef CI_SUPPRESS_KNOWN_ISSUES |
199 | while (boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() < 200) | |
200 | #else | |
201 | while (boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() < 50) | |
202 | #endif | |
7c673cae | 203 | { |
92f5a8d4 | 204 | Rat rat(generate_random_int<i_type>(), generate_random_int<i_type>()); |
7c673cae | 205 | Float f(rat); |
92f5a8d4 TL |
206 | Rat new_rat(f); // rounded value |
207 | int c = new_rat.compare(rat); | |
208 | if (c < 0) | |
7c673cae FG |
209 | { |
210 | // If f was rounded down, next float up must be above the original value: | |
211 | f = boost::math::float_next(f); | |
212 | new_rat.assign(f); | |
213 | BOOST_CHECK(new_rat >= rat); | |
214 | } | |
92f5a8d4 | 215 | else if (c > 0) |
7c673cae FG |
216 | { |
217 | // If f was rounded up, next float down must be below the original value: | |
218 | f = boost::math::float_prior(f); | |
219 | new_rat.assign(f); | |
220 | BOOST_CHECK(new_rat <= rat); | |
221 | } | |
222 | else | |
223 | { | |
224 | // Values were equal... nothing to test. | |
225 | } | |
92f5a8d4 | 226 | if (boost::detail::test_errors() > 100) |
7c673cae FG |
227 | break; |
228 | } | |
229 | ||
230 | std::cout << "Execution time = " << boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() << "s" << std::endl; | |
231 | std::cout << "Total values tested: " << count << std::endl; | |
7c673cae FG |
232 | } |
233 | ||
234 | #if defined(TEST2) | |
235 | ||
236 | void double_spot_tests() | |
237 | { | |
238 | boost::multiprecision::cpp_rational rat = 1; | |
239 | boost::multiprecision::cpp_rational twiddle(boost::multiprecision::cpp_int(1), boost::multiprecision::cpp_int(boost::multiprecision::cpp_int(1) << 54)); | |
240 | rat += boost::multiprecision::cpp_rational(boost::multiprecision::cpp_int(1), boost::multiprecision::cpp_int(boost::multiprecision::cpp_int(1) << 50)); | |
241 | ||
242 | double d = rat.convert_to<double>(); | |
243 | ||
244 | rat += twiddle; | |
245 | BOOST_CHECK_EQUAL(d, rat.convert_to<double>()); | |
246 | rat += twiddle; | |
247 | // tie: round to even rounds down | |
248 | BOOST_CHECK_EQUAL(d, rat.convert_to<double>()); | |
249 | rat += twiddle; | |
250 | BOOST_CHECK_NE(d, rat.convert_to<double>()); | |
251 | rat -= twiddle; | |
252 | BOOST_CHECK_EQUAL(d, rat.convert_to<double>()); | |
253 | rat += boost::multiprecision::cpp_rational(boost::multiprecision::cpp_int(1), boost::multiprecision::cpp_int(boost::multiprecision::cpp_int(1) << 52)); | |
254 | // tie, but last bit is now a 1 so we round up: | |
255 | BOOST_CHECK_NE(d, rat.convert_to<double>()); | |
7c673cae FG |
256 | } |
257 | ||
258 | #endif | |
259 | ||
260 | int main() | |
261 | { | |
262 | using namespace boost::multiprecision; | |
263 | #if defined(TEST1) && !defined(BOOST_MSVC) | |
1e59de90 | 264 | test_round_trip<number<cpp_bin_float<113, digit_base_2, void, std::int16_t> >, cpp_rational>(); |
7c673cae FG |
265 | #elif defined(TEST2) |
266 | double_spot_tests(); | |
267 | test_round_trip<double, cpp_rational>(); | |
268 | #elif defined(TEST3) && !defined(BOOST_MSVC) | |
1e59de90 | 269 | test_random_rationals<number<cpp_bin_float<113, digit_base_2, void, std::int16_t> >, cpp_rational>(); |
7c673cae FG |
270 | #elif defined(TEST4) |
271 | test_random_rationals<double, cpp_rational>(); | |
272 | #elif defined(TEST5) | |
273 | // This does not work: gmp does not correctly round integer to float or | |
274 | // rational to float conversions: | |
275 | test_round_trip<double, mpq_rational>(); | |
276 | #elif defined(TEST6) | |
277 | test_round_trip<mpfr_float_100, mpq_rational>(); | |
278 | #elif defined(TEST7) | |
279 | test_random_rationals<mpfr_float_100, mpq_rational>(); | |
280 | #elif defined(TEST8) | |
281 | test_random_rationals<double, mpq_rational>(); | |
282 | #endif | |
283 | return boost::report_errors(); | |
284 | } |