mirror of
https://github.com/boostorg/multiprecision.git
synced 2026-01-19 04:22:11 +00:00
More cover dec_float and improve comments
This commit is contained in:
@@ -1645,7 +1645,7 @@ long double cpp_dec_float<Digits10, ExponentType, Allocator>::extract_long_doubl
|
||||
// Check if *this cpp_dec_float<Digits10, ExponentType, Allocator> exceeds the maximum of double.
|
||||
if (xx.compare(long_double_max()) > 0)
|
||||
{
|
||||
return ((!neg) ? std::numeric_limits<long double>::infinity()
|
||||
return ((!neg) ? std::numeric_limits<long double>::infinity()
|
||||
: -std::numeric_limits<long double>::infinity());
|
||||
}
|
||||
|
||||
@@ -1795,7 +1795,7 @@ int128_type cpp_dec_float<Digits10, ExponentType, Allocator>::extract_signed_int
|
||||
}
|
||||
else
|
||||
{
|
||||
// Extract the data into an unsigned long long value.
|
||||
// Extract the data into an (unsigned) boost::uint128_type value.
|
||||
cpp_dec_float<Digits10, ExponentType, Allocator> xn(extract_integer_part());
|
||||
if (xn.isneg())
|
||||
xn.negate();
|
||||
@@ -1817,9 +1817,9 @@ int128_type cpp_dec_float<Digits10, ExponentType, Allocator>::extract_signed_int
|
||||
}
|
||||
else
|
||||
{
|
||||
// This strange expression avoids a hardware trap in the corner case
|
||||
// that val is the most negative value permitted in long long.
|
||||
// See https://svn.boost.org/trac/boost/ticket/9740.
|
||||
// This strange expression avoids a hardware trap in the corner case that
|
||||
// val is the most negative value permitted in (signed) boost::int128_type.
|
||||
// See also https://svn.boost.org/trac/boost/ticket/9740.
|
||||
//
|
||||
int128_type sval = static_cast<int128_type>(val - 1);
|
||||
sval = -sval;
|
||||
@@ -3157,7 +3157,7 @@ cpp_dec_float<Digits10, ExponentType, Allocator> cpp_dec_float<Digits10, Exponen
|
||||
default_ops::detail::pow_imp(t, cpp_dec_float<Digits10, ExponentType, Allocator>::two(), static_cast<unsigned long long>(p), std::integral_constant<bool, false>());
|
||||
|
||||
return t;
|
||||
}
|
||||
} // LCOV_EXCL_LINE
|
||||
|
||||
template <unsigned Digits10, class ExponentType, class Allocator>
|
||||
inline void eval_add(cpp_dec_float<Digits10, ExponentType, Allocator>& result, const cpp_dec_float<Digits10, ExponentType, Allocator>& o)
|
||||
|
||||
@@ -2219,10 +2219,12 @@ constexpr auto eval_convert_to(signed long long* result, const cpp_double_fp_bac
|
||||
// this workaround will actually break the long long conversion tests
|
||||
// on those platforms.
|
||||
//
|
||||
// Our assumption is that on x64 there is x87 math (double -> long double) being performed in the background
|
||||
// which would aid the conversion of double value to long long
|
||||
// Our assumption is that on x64 there is x87 math (double -> long double)
|
||||
// being performed in the background. Seemingly these might "aid" the conversion
|
||||
// of double value to long long. Somehow I get the feeling this issue will arise
|
||||
// in future evolution of the cpp_double_fp_backend.
|
||||
//
|
||||
// This workaround has been tested on: ARM64 (linux and mac), s390x and PPC64LE
|
||||
// This workaround has been tested on: ARM64 (linux and mac), s390x and PPC64LE.
|
||||
|
||||
constexpr bool
|
||||
needs_workaround
|
||||
|
||||
@@ -179,6 +179,7 @@ test-suite arithmetic_tests :
|
||||
|
||||
[ run test_cpp_dec_float_round.cpp no_eh_support ]
|
||||
[ run test_various_edges.cpp no_eh_support ]
|
||||
[ run test_various_edges_cpp_dec_float.cpp no_eh_support ]
|
||||
|
||||
[ run test_arithmetic_logged_1.cpp no_eh_support : : : <toolset>msvc:<cxxflags>-bigobj [ check-target-builds ../config//has_float128 : <source>quadmath ] ]
|
||||
[ run test_arithmetic_logged_2.cpp no_eh_support : : : <toolset>msvc:<cxxflags>-bigobj [ check-target-builds ../config//has_float128 : <source>quadmath ] ]
|
||||
|
||||
@@ -36,7 +36,7 @@ do to add all the "template<...>" prefixes to the functions.
|
||||
struct skeleton_backend
|
||||
{
|
||||
//
|
||||
// Each backend need to declare 3 type lists which declare the types
|
||||
// Each backend needs to declare 3 type lists which declare the types
|
||||
// with which this can interoperate. These lists must at least contain
|
||||
// the widest type in each category - so "long long" must be the final
|
||||
// type in the signed_types list for example. Any narrower types if not
|
||||
|
||||
@@ -1625,3 +1625,9 @@ template<typename FloatType> auto my_one () noexcept -> FloatType& { using float
|
||||
template<typename FloatType> auto my_inf () noexcept -> FloatType& { using float_type = FloatType; static float_type val_inf { std::numeric_limits<float_type>::infinity() }; return val_inf; }
|
||||
template<typename FloatType> auto my_nan () noexcept -> FloatType& { using float_type = FloatType; static float_type val_nan { std::numeric_limits<float_type>::quiet_NaN() }; return val_nan; }
|
||||
template<typename FloatType> auto my_exp1() noexcept -> FloatType& { using float_type = FloatType; static float_type val_exp1 { "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921817413596629043572900334295260595630738132328627943490763233829880753195251019011573834187930702154089149934884167509244761460668082265" }; return val_exp1; }
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
260
test/test_various_edges_cpp_dec_float.cpp
Normal file
260
test/test_various_edges_cpp_dec_float.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
// Copyright 2023 - 2025 Matt Borland
|
||||
// Copyright 2023 - 2025 Christopher Kormanyos
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
// Some parts of this test file have been taken from the Boost.Decimal project.
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/multiprecision/cpp_dec_float.hpp>
|
||||
|
||||
#include <test_traits.hpp> // Note: include this AFTER the test-backends are defined
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wfloat-equal"
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
template<typename FloatType> auto my_zero() noexcept -> FloatType&;
|
||||
template<typename FloatType> auto my_one () noexcept -> FloatType&;
|
||||
template<typename FloatType> auto my_inf () noexcept -> FloatType&;
|
||||
template<typename FloatType> auto my_nan () noexcept -> FloatType&;
|
||||
|
||||
namespace local
|
||||
{
|
||||
template<typename IntegralTimePointType,
|
||||
typename ClockType = std::chrono::high_resolution_clock>
|
||||
auto time_point() noexcept -> IntegralTimePointType
|
||||
{
|
||||
using local_integral_time_point_type = IntegralTimePointType;
|
||||
using local_clock_type = ClockType;
|
||||
|
||||
const auto current_now =
|
||||
static_cast<std::uintmax_t>
|
||||
(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>
|
||||
(
|
||||
local_clock_type::now().time_since_epoch()
|
||||
).count()
|
||||
);
|
||||
|
||||
return static_cast<local_integral_time_point_type>(current_now);
|
||||
}
|
||||
|
||||
template<typename NumericType>
|
||||
auto is_close_fraction(const NumericType& a,
|
||||
const NumericType& b,
|
||||
const NumericType& tol) noexcept -> bool
|
||||
{
|
||||
using std::fabs;
|
||||
|
||||
auto result_is_ok = bool { };
|
||||
|
||||
NumericType delta { };
|
||||
|
||||
if(b == static_cast<NumericType>(0))
|
||||
{
|
||||
delta = fabs(a - b); // LCOV_EXCL_LINE
|
||||
|
||||
result_is_ok = (delta < tol); // LCOV_EXCL_LINE
|
||||
}
|
||||
else
|
||||
{
|
||||
delta = fabs(1 - (a / b));
|
||||
|
||||
result_is_ok = (delta < tol);
|
||||
}
|
||||
|
||||
return result_is_ok;
|
||||
}
|
||||
|
||||
template<class FloatType>
|
||||
bool test_convert()
|
||||
{
|
||||
using float_type = FloatType;
|
||||
|
||||
std::mt19937_64 gen { time_point<typename std::mt19937_64::result_type>() };
|
||||
|
||||
auto dis =
|
||||
std::uniform_real_distribution<float>
|
||||
{
|
||||
static_cast<float>(1.01F),
|
||||
static_cast<float>(1.04F)
|
||||
};
|
||||
|
||||
bool result_is_ok { true };
|
||||
|
||||
using std::isinf;
|
||||
using std::isnan;
|
||||
using std::signbit;
|
||||
|
||||
{
|
||||
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(16)); ++index)
|
||||
{
|
||||
static_cast<void>(index);
|
||||
|
||||
float_type flt_non_finite { };
|
||||
long double ldbl_non_finite { };
|
||||
bool result_non_finite_is_ok { };
|
||||
|
||||
flt_non_finite = std::numeric_limits<float_type>::quiet_NaN() * dis(gen);
|
||||
ldbl_non_finite = static_cast<long double>(flt_non_finite);
|
||||
result_non_finite_is_ok = isnan(ldbl_non_finite);
|
||||
BOOST_TEST(result_non_finite_is_ok);
|
||||
result_is_ok = (result_non_finite_is_ok && result_is_ok);
|
||||
|
||||
flt_non_finite = std::numeric_limits<float_type>::infinity() * dis(gen);
|
||||
ldbl_non_finite = static_cast<long double>(flt_non_finite);
|
||||
result_non_finite_is_ok = isinf(ldbl_non_finite);
|
||||
BOOST_TEST(result_non_finite_is_ok);
|
||||
result_is_ok = (result_non_finite_is_ok && result_is_ok);
|
||||
|
||||
flt_non_finite = std::numeric_limits<float_type>::infinity() * -dis(gen);
|
||||
ldbl_non_finite = static_cast<long double>(flt_non_finite);
|
||||
result_non_finite_is_ok = (isinf(ldbl_non_finite) && signbit(ldbl_non_finite));
|
||||
BOOST_TEST(result_non_finite_is_ok);
|
||||
result_is_ok = (result_non_finite_is_ok && result_is_ok);
|
||||
|
||||
flt_non_finite = float_type { (std::numeric_limits<long double>::max)() }
|
||||
* float_type { (std::numeric_limits<long double>::max)() }
|
||||
* dis(gen);
|
||||
ldbl_non_finite = static_cast<long double>(flt_non_finite);
|
||||
result_non_finite_is_ok = isinf(ldbl_non_finite);
|
||||
BOOST_TEST(result_non_finite_is_ok);
|
||||
result_is_ok = (result_non_finite_is_ok && result_is_ok);
|
||||
|
||||
flt_non_finite = float_type { (std::numeric_limits<long double>::max)() }
|
||||
* float_type { (std::numeric_limits<long double>::max)() }
|
||||
* -dis(gen);
|
||||
ldbl_non_finite = static_cast<long double>(flt_non_finite);
|
||||
result_non_finite_is_ok = (isinf(ldbl_non_finite) && signbit(ldbl_non_finite));
|
||||
BOOST_TEST(result_non_finite_is_ok);
|
||||
result_is_ok = (result_non_finite_is_ok && result_is_ok);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BOOST_HAS_INT128
|
||||
{
|
||||
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(64)); ++index)
|
||||
{
|
||||
float_type
|
||||
flt_n128
|
||||
{
|
||||
float_type { (std::numeric_limits<signed long long>::max)() }
|
||||
* float_type { dis(gen) * 12345.0F }
|
||||
* float_type { dis(gen) * dis(gen) }
|
||||
};
|
||||
|
||||
const bool is_neg { ((index & 1U) != 0U) };
|
||||
|
||||
if(is_neg)
|
||||
{
|
||||
flt_n128 = -flt_n128;
|
||||
}
|
||||
|
||||
boost::int128_type val_n128 { static_cast<boost::int128_type>(flt_n128) };
|
||||
|
||||
bool result_val_n128_is_ok { };
|
||||
|
||||
const std::string str_n128 { boost::lexical_cast<std::string>(val_n128) };
|
||||
|
||||
const std::string
|
||||
str_ctrl
|
||||
{
|
||||
[&flt_n128]()
|
||||
{
|
||||
std::stringstream strm { };
|
||||
|
||||
strm << std::fixed << flt_n128;
|
||||
|
||||
std::string str_local { strm.str() };
|
||||
|
||||
str_local = str_local.substr(std::size_t { UINT8_C(0) }, str_local.find('.'));
|
||||
|
||||
return str_local;
|
||||
}()
|
||||
};
|
||||
|
||||
result_val_n128_is_ok =
|
||||
(
|
||||
(str_n128 == str_ctrl)
|
||||
&& (
|
||||
(!is_neg) ? (val_n128 > static_cast<boost::int128_type>((std::numeric_limits<signed long long>::max)()))
|
||||
: (val_n128 < static_cast<boost::int128_type>((std::numeric_limits<signed long long>::max)()))
|
||||
)
|
||||
);
|
||||
|
||||
BOOST_TEST(result_val_n128_is_ok);
|
||||
|
||||
result_is_ok = (result_val_n128_is_ok && result_is_ok);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(32)); ++index)
|
||||
{
|
||||
constexpr boost::int128_type my_max_val_n128 = (((static_cast<boost::int128_type>(1) << (sizeof(boost::int128_type) * CHAR_BIT - 2)) - 1) << 1) + 1;
|
||||
constexpr boost::int128_type my_min_val_n128 = static_cast<boost::int128_type>(-my_max_val_n128 - 1);
|
||||
|
||||
const float_type
|
||||
flt_n128
|
||||
{
|
||||
float_type { -my_max_val_n128 }
|
||||
- ::my_one<float_type>()
|
||||
+ float_type { ::my_zero<float_type>() + float_type { dis(gen) } }
|
||||
- ::my_one<float_type>()
|
||||
- ::my_one<float_type>()
|
||||
};
|
||||
|
||||
const boost::int128_type val_n128 { static_cast<boost::int128_type>(flt_n128) };
|
||||
|
||||
const bool result_val_n128_is_ok { (val_n128 == my_min_val_n128) };
|
||||
|
||||
BOOST_TEST(result_val_n128_is_ok);
|
||||
|
||||
result_is_ok = (result_val_n128_is_ok && result_is_ok);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return result_is_ok;
|
||||
}
|
||||
|
||||
} // namespace local
|
||||
|
||||
auto main() -> int
|
||||
{
|
||||
{
|
||||
using float_backend_type = boost::multiprecision::cpp_dec_float<50>;
|
||||
|
||||
using float_type = boost::multiprecision::number<float_backend_type, boost::multiprecision::et_off>;
|
||||
|
||||
std::cout << "Testing type: " << typeid(float_type).name() << std::endl;
|
||||
|
||||
static_cast<void>(local::test_convert<float_type>());
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
template<typename FloatType> auto my_zero() noexcept -> FloatType& { using float_type = FloatType; static float_type val_zero { 0 }; return val_zero; }
|
||||
template<typename FloatType> auto my_one () noexcept -> FloatType& { using float_type = FloatType; static float_type val_one { 1 }; return val_one; }
|
||||
template<typename FloatType> auto my_inf () noexcept -> FloatType& { using float_type = FloatType; static float_type val_inf { std::numeric_limits<float_type>::infinity() }; return val_inf; }
|
||||
template<typename FloatType> auto my_nan () noexcept -> FloatType& { using float_type = FloatType; static float_type val_nan { std::numeric_limits<float_type>::quiet_NaN() }; return val_nan; }
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
Reference in New Issue
Block a user