160 lines
4.3 KiB
C++
160 lines
4.3 KiB
C++
// Copyright 2018 John Maddock. Distributed under the Boost
|
|
// Software License, Version 1.0. (See accompanying file
|
|
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
#include <boost/multiprecision/cpp_bin_float.hpp>
|
|
#include <boost/math/special_functions.hpp>
|
|
#include <boost/chrono.hpp>
|
|
#include <boost/random/mersenne_twister.hpp>
|
|
#include <boost/random/uniform_int.hpp>
|
|
|
|
template <class Clock>
|
|
struct stopwatch
|
|
{
|
|
typedef typename Clock::duration duration;
|
|
stopwatch()
|
|
{
|
|
m_start = Clock::now();
|
|
}
|
|
duration elapsed()
|
|
{
|
|
return Clock::now() - m_start;
|
|
}
|
|
void reset()
|
|
{
|
|
m_start = Clock::now();
|
|
}
|
|
|
|
private:
|
|
typename Clock::time_point m_start;
|
|
};
|
|
|
|
template <class T>
|
|
T generate_random()
|
|
{
|
|
typedef int e_type;
|
|
static boost::random::mt19937 gen;
|
|
T val = gen();
|
|
T prev_val = -1;
|
|
while (val != prev_val)
|
|
{
|
|
val *= (gen.max)();
|
|
prev_val = val;
|
|
val += gen();
|
|
}
|
|
e_type e;
|
|
val = frexp(val, &e);
|
|
|
|
static boost::random::uniform_int_distribution<e_type> ui(-20, 20);
|
|
return ldexp(val, ui(gen));
|
|
}
|
|
|
|
template <typename T>
|
|
double my_convert_to_double(const T& x)
|
|
{
|
|
double ret = 0;
|
|
if (isfinite(x))
|
|
{
|
|
if (x.backend().exponent() >= -1023 - 52 && x != 0)
|
|
{
|
|
if (x.backend().exponent() <= 1023)
|
|
{
|
|
int e = x.backend().exponent();
|
|
T y = ldexp(abs(x), 55 - e);
|
|
T t = trunc(y);
|
|
int64_t ti = t.template convert_to<int64_t>();
|
|
if ((ti & 1) == 0)
|
|
{
|
|
if (t < y)
|
|
ti |= 1;
|
|
}
|
|
if (e >= -1023 + 1)
|
|
{
|
|
ret = ldexp(double(ti), e - 55);
|
|
}
|
|
else
|
|
{
|
|
// subnormal
|
|
typedef boost::multiprecision::number<boost::multiprecision::cpp_bin_float<128, boost::multiprecision::backends::digit_base_2> > cpp_bin_float128_t;
|
|
cpp_bin_float128_t sx = ldexp(cpp_bin_float128_t(ti), e - 55);
|
|
sx += DBL_MIN;
|
|
e = -1023 + 1;
|
|
cpp_bin_float128_t sy = ldexp(sx, 55 - e);
|
|
cpp_bin_float128_t st = trunc(sy);
|
|
ti = st.convert_to<int64_t>();
|
|
if ((ti & 1) == 0)
|
|
{
|
|
if (st < sy)
|
|
ti |= 1;
|
|
}
|
|
ret = ldexp(double(ti), e - 55) - DBL_MIN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// overflow
|
|
ret = HUGE_VAL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isnan(x))
|
|
return nan("");
|
|
// inf
|
|
ret = HUGE_VAL;
|
|
}
|
|
return x.backend().sign() ? -ret : ret;
|
|
}
|
|
|
|
template <class T>
|
|
void test_conversion_time(const char* name)
|
|
{
|
|
std::cout << "Testing times for type: " << name << "\n";
|
|
std::vector<T> values;
|
|
|
|
for (unsigned i = 0; i < 10000000; ++i)
|
|
{
|
|
values.push_back(generate_random<T>());
|
|
}
|
|
|
|
boost::chrono::duration<double> time;
|
|
stopwatch<boost::chrono::high_resolution_clock> c;
|
|
|
|
double total = 0;
|
|
|
|
for (typename std::vector<T>::const_iterator i = values.begin(); i != values.end(); ++i)
|
|
{
|
|
total += my_convert_to_double(*i);
|
|
}
|
|
|
|
time = c.elapsed();
|
|
std::cout << std::setprecision(3) << std::fixed;
|
|
std::cout << "Reference time: " << std::setw(7) << std::right << time << " (total sum = " << total << ")" << std::endl;
|
|
|
|
c.reset();
|
|
|
|
total = 0;
|
|
|
|
for (typename std::vector<T>::const_iterator i = values.begin(); i != values.end(); ++i)
|
|
{
|
|
total += i->template convert_to<double>();
|
|
}
|
|
|
|
time = c.elapsed();
|
|
std::cout << "Boost time: " << std::setw(7) << std::right << time << " (total sum = " << total << ")" << std::endl;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
using namespace boost::multiprecision;
|
|
|
|
test_conversion_time<cpp_bin_float_double>("cpp_bin_float_double");
|
|
test_conversion_time<cpp_bin_float_quad>("cpp_bin_float_quad");
|
|
test_conversion_time<cpp_bin_float_oct>("cpp_bin_float_oct");
|
|
test_conversion_time<cpp_bin_float_50>("cpp_bin_float_50");
|
|
test_conversion_time<cpp_bin_float_100>("cpp_bin_float_100");
|
|
|
|
return 0;
|
|
}
|