1 // Copyright 2018 John Maddock. Distributed under the Boost
2 // Software License, Version 1.0. (See accompanying file
3 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
4
5 #include <boost/multiprecision/cpp_bin_float.hpp>
6 #include <boost/math/special_functions.hpp>
7 #include <boost/chrono.hpp>
8 #include <boost/random/mersenne_twister.hpp>
9 #include <boost/random/uniform_int.hpp>
10
11 template <class Clock>
12 struct stopwatch
13 {
14 typedef typename Clock::duration duration;
stopwatchstopwatch15 stopwatch()
16 {
17 m_start = Clock::now();
18 }
elapsedstopwatch19 duration elapsed()
20 {
21 return Clock::now() - m_start;
22 }
resetstopwatch23 void reset()
24 {
25 m_start = Clock::now();
26 }
27
28 private:
29 typename Clock::time_point m_start;
30 };
31
32 template <class T>
generate_random()33 T generate_random()
34 {
35 typedef int e_type;
36 static boost::random::mt19937 gen;
37 T val = gen();
38 T prev_val = -1;
39 while (val != prev_val)
40 {
41 val *= (gen.max)();
42 prev_val = val;
43 val += gen();
44 }
45 e_type e;
46 val = frexp(val, &e);
47
48 static boost::random::uniform_int_distribution<e_type> ui(-20, 20);
49 return ldexp(val, ui(gen));
50 }
51
52 template <typename T>
my_convert_to_double(const T & x)53 double my_convert_to_double(const T& x)
54 {
55 double ret = 0;
56 if (isfinite(x))
57 {
58 if (x.backend().exponent() >= -1023 - 52 && x != 0)
59 {
60 if (x.backend().exponent() <= 1023)
61 {
62 int e = x.backend().exponent();
63 T y = ldexp(abs(x), 55 - e);
64 T t = trunc(y);
65 int64_t ti = t.template convert_to<int64_t>();
66 if ((ti & 1) == 0)
67 {
68 if (t < y)
69 ti |= 1;
70 }
71 if (e >= -1023 + 1)
72 {
73 ret = ldexp(double(ti), e - 55);
74 }
75 else
76 {
77 // subnormal
78 typedef boost::multiprecision::number<boost::multiprecision::cpp_bin_float<128, boost::multiprecision::backends::digit_base_2> > cpp_bin_float128_t;
79 cpp_bin_float128_t sx = ldexp(cpp_bin_float128_t(ti), e - 55);
80 sx += DBL_MIN;
81 e = -1023 + 1;
82 cpp_bin_float128_t sy = ldexp(sx, 55 - e);
83 cpp_bin_float128_t st = trunc(sy);
84 ti = st.convert_to<int64_t>();
85 if ((ti & 1) == 0)
86 {
87 if (st < sy)
88 ti |= 1;
89 }
90 ret = ldexp(double(ti), e - 55) - DBL_MIN;
91 }
92 }
93 else
94 {
95 // overflow
96 ret = HUGE_VAL;
97 }
98 }
99 }
100 else
101 {
102 if (isnan(x))
103 return nan("");
104 // inf
105 ret = HUGE_VAL;
106 }
107 return x.backend().sign() ? -ret : ret;
108 }
109
110 template <class T>
test_conversion_time(const char * name)111 void test_conversion_time(const char* name)
112 {
113 std::cout << "Testing times for type: " << name << "\n";
114 std::vector<T> values;
115
116 for (unsigned i = 0; i < 10000000; ++i)
117 {
118 values.push_back(generate_random<T>());
119 }
120
121 boost::chrono::duration<double> time;
122 stopwatch<boost::chrono::high_resolution_clock> c;
123
124 double total = 0;
125
126 for (typename std::vector<T>::const_iterator i = values.begin(); i != values.end(); ++i)
127 {
128 total += my_convert_to_double(*i);
129 }
130
131 time = c.elapsed();
132 std::cout << std::setprecision(3) << std::fixed;
133 std::cout << "Reference time: " << std::setw(7) << std::right << time << " (total sum = " << total << ")" << std::endl;
134
135 c.reset();
136
137 total = 0;
138
139 for (typename std::vector<T>::const_iterator i = values.begin(); i != values.end(); ++i)
140 {
141 total += i->template convert_to<double>();
142 }
143
144 time = c.elapsed();
145 std::cout << "Boost time: " << std::setw(7) << std::right << time << " (total sum = " << total << ")" << std::endl;
146 }
147
main()148 int main()
149 {
150 using namespace boost::multiprecision;
151
152 test_conversion_time<cpp_bin_float_double>("cpp_bin_float_double");
153 test_conversion_time<cpp_bin_float_quad>("cpp_bin_float_quad");
154 test_conversion_time<cpp_bin_float_oct>("cpp_bin_float_oct");
155 test_conversion_time<cpp_bin_float_50>("cpp_bin_float_50");
156 test_conversion_time<cpp_bin_float_100>("cpp_bin_float_100");
157
158 return 0;
159 }
160