1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
// Copyright John Maddock 2022.
// Use, modification and distribution are subject to 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)
#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#endif
#define BOOST_CHRONO_HEADER_ONLY
#ifdef TEST_BIN_FLOAT
#include <boost/multiprecision/cpp_bin_float.hpp>
#endif
#ifdef TEST_DEC_FLOAT
#include <boost/multiprecision/cpp_dec_float.hpp>
#endif
#ifdef TEST_MPFR
#include <boost/multiprecision/mpfr.hpp>
#endif
#ifdef TEST_MPF
#include <boost/multiprecision/gmp.hpp>
#endif
#ifdef TEST_FLOAT128
#include <boost/multiprecision/float128.hpp>
#endif
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/chrono.hpp>
#include "test.hpp"
#include <array>
#include <iostream>
#include <iomanip>
#ifdef BOOST_MSVC
#pragma warning(disable : 4127)
#endif
#if defined(TEST_MPF)
template <unsigned N, boost::multiprecision::expression_template_option ET>
bool has_bad_bankers_rounding(const boost::multiprecision::number<boost::multiprecision::gmp_float<N>, ET>&)
{
return true;
}
#endif
template <class T>
bool has_bad_bankers_rounding(const T&)
{
return false;
}
bool is_bankers_rounding_error(const std::string& s, const std::string& expect)
{
// This check isn't foolproof: that would require *much* more sophisticated code!!!
std::string::size_type l = expect.size();
if (l != s.size())
return false;
std::string::size_type len = s.find('e');
if (len == std::string::npos)
len = l - 1;
else
--len;
for (unsigned i = 0; i < len; ++i)
{
char c1 = s[i];
char c2 = expect[i];
if (c1 != c2)
return false;
}
if (s[len] != expect[len] + 1)
return false;
return true;
}
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>
struct exponent_type
{
typedef int type;
};
template <class T, boost::multiprecision::expression_template_option ET>
struct exponent_type<boost::multiprecision::number<T, ET> >
{
typedef typename T::exponent_type type;
};
template <class T>
T generate_random_float()
{
BOOST_MATH_STD_USING
typedef typename exponent_type<T>::type 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 const int max_exponent_value = (std::min)(static_cast<int>(std::numeric_limits<T>::max_exponent - std::numeric_limits<T>::digits - 20), 40);
static boost::random::uniform_int_distribution<e_type> ui(0, max_exponent_value);
return ldexp(val, ui(gen));
}
template <class Float>
void test_fixed_io()
{
std::cout << "Testing type " << typeid(Float).name() << std::endl;
std::cout << "digits = " << std::numeric_limits<Float>::digits << std::endl;
std::cout << "digits10 = " << std::numeric_limits<Float>::digits10 << std::endl;
std::cout << "max_digits10 = " << std::numeric_limits<Float>::max_digits10 << std::endl;
stopwatch<boost::chrono::high_resolution_clock> w;
int count = 0;
#ifndef CI_SUPPRESS_KNOWN_ISSUES
while (boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() < 200)
#else
while (boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() < 50)
#endif
{
double val = generate_random_float<double>();
Float f(val);
#ifdef BOOST_MSVC
if (val > 0.5 && val < 1)
val = 1;
#endif
std::stringstream ss1, ss2;
ss1 << std::fixed << std::setprecision(0) << val;
ss2 << std::fixed << std::setprecision(0) << f;
if (ss1.str() != ss2.str())
{
if (has_bad_bankers_rounding(Float()) && is_bankers_rounding_error(ss2.str(), ss1.str()))
{
std::cout << "Ignoring bankers-rounding error with GMP mp_f.\n";
}
else
{
std::cout << std::setprecision(20) << "Testing value " << val << std::endl;
std::cout << "Got: " << ss2.str() << std::endl;
std::cout << "Expected: " << ss1.str() << std::endl;
++boost::detail::test_errors();
Float(val).str(0, std::ios_base::fixed);
}
}
++count;
if (boost::detail::test_errors() > 100)
break;
}
std::cout << "Execution time = " << boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count() << "s" << std::endl;
std::cout << "Total values tested: " << count << std::endl;
}
int main()
{
#if defined(BOOST_MSVC) && (BOOST_MSVC < 1920)
std::cout << "MSVC prior to 14.2 does not perform bankers rounding for double IO, as a result all our test cases are incorrect, so there's nothing we can productively test here." << std::endl;
#else
using namespace boost::multiprecision;
#ifdef TEST_BIN_FLOAT
test_fixed_io<number<cpp_bin_float<113, digit_base_2, void, std::int16_t> > >();
#endif
#ifdef TEST_DEC_FLOAT
test_fixed_io<cpp_dec_float_50>();
#endif
#ifdef TEST_MPFR
test_fixed_io<boost::multiprecision::mpfr_float_50>();
#endif
#ifdef TEST_MPF
test_fixed_io<boost::multiprecision::mpf_float_50>();
#endif
#ifdef TEST_FLOAT128
test_fixed_io<boost::multiprecision::float128>();
#endif
#endif
return boost::report_errors();
}
|