Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions include/boost/decimal/decimal128_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -926,12 +926,20 @@ BOOST_DECIMAL_CUDA_CONSTEXPR decimal128_t::decimal128_t(T1 coeff, T2 exp, const
exp -= available_space;
*this = detail::pack_in_range<decimal128_t>(reduced_coeff, exp, is_negative);
}
else if (detail::overflow_is_finite(is_negative))
{
*this = detail::pack_in_range<decimal128_t>(detail::d128_max_significand_value, detail::max_biased_exp_v<decimal128_t> - detail::bias_v<decimal128_t>, is_negative);
}
else
{
bits_ = detail::d128_inf_mask;
bits_.high |= is_negative ? detail::d128_sign_mask : UINT64_C(0);
}
}
else if (exp >= 0 && detail::overflow_is_finite(is_negative))
{
*this = detail::pack_in_range<decimal128_t>(detail::d128_max_significand_value, detail::max_biased_exp_v<decimal128_t> - detail::bias_v<decimal128_t>, is_negative);
}
else
{
bits_ = exp < 0 ? zero : detail::d128_inf_mask;
Expand Down Expand Up @@ -1103,14 +1111,7 @@ BOOST_DECIMAL_CXX20_CONSTEXPR decimal128_t::decimal128_t(const Float val) noexce
<< "\nSign: " << components.sign << std::endl;
#endif

if (components.exponent > detail::emax_v<decimal128_t>)
{
*this = from_bits(detail::d128_inf_mask);
}
else
{
*this = decimal128_t {components.mantissa, components.exponent, components.sign};
}
*this = decimal128_t {components.mantissa, components.exponent, components.sign};
}
}

Expand Down
19 changes: 9 additions & 10 deletions include/boost/decimal/decimal32_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,12 +820,20 @@ BOOST_DECIMAL_CUDA_CONSTEXPR decimal32_t::decimal32_t(T1 coeff, T2 exp, const de
exp -= available_space;
*this = detail::pack_in_range<decimal32_t>(reduced_coeff, exp, is_negative);
}
else if (detail::overflow_is_finite(is_negative))
{
*this = detail::pack_in_range<decimal32_t>(detail::d32_max_significand_value, detail::max_biased_exp_v<decimal32_t> - detail::bias, is_negative);
}
else
{
bits_ = detail::d32_inf_mask;
bits_ |= is_negative ? detail::d32_sign_mask : UINT32_C(0);
}
}
else if (exp >= 0 && detail::overflow_is_finite(is_negative))
{
*this = detail::pack_in_range<decimal32_t>(detail::d32_max_significand_value, detail::max_biased_exp_v<decimal32_t> - detail::bias, is_negative);
}
else
{
// Reset the value and make sure to preserve the sign of 0/inf
Expand Down Expand Up @@ -1850,16 +1858,7 @@ BOOST_DECIMAL_CXX20_CONSTEXPR decimal32_t::decimal32_t(const Float val) noexcept
<< "\nSign: " << components.sign << std::endl;
#endif

#ifndef BOOST_DECIMAL_FAST_MATH
if (components.exponent > detail::emax)
{
*this = boost::decimal::from_bits(boost::decimal::detail::d32_inf_mask);
}
else
#endif
{
*this = decimal32_t {components.mantissa, components.exponent, components.sign};
}
*this = decimal32_t {components.mantissa, components.exponent, components.sign};
}
}

Expand Down
17 changes: 9 additions & 8 deletions include/boost/decimal/decimal64_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,12 +857,20 @@ BOOST_DECIMAL_CUDA_CONSTEXPR decimal64_t::decimal64_t(T1 coeff, T2 exp, const de
exp -= available_space;
*this = detail::pack_in_range<decimal64_t>(reduced_coeff, exp, is_negative);
}
else if (detail::overflow_is_finite(is_negative))
{
*this = detail::pack_in_range<decimal64_t>(detail::d64_max_significand_value, detail::max_biased_exp_v<decimal64_t> - detail::bias_v<decimal64_t>, is_negative);
}
else
{
bits_ = exp < 0 ? UINT64_C(0) : detail::d64_inf_mask;
bits_ |= is_negative ? detail::d64_sign_mask : UINT64_C(0);
}
}
else if (exp >= 0 && detail::overflow_is_finite(is_negative))
{
*this = detail::pack_in_range<decimal64_t>(detail::d64_max_significand_value, detail::max_biased_exp_v<decimal64_t> - detail::bias_v<decimal64_t>, is_negative);
}
else
{
// Reset the value and make sure to preserve the sign of 0/inf
Expand Down Expand Up @@ -1031,14 +1039,7 @@ BOOST_DECIMAL_CXX20_CONSTEXPR decimal64_t::decimal64_t(const Float val) noexcept
<< "\nSign: " << components.sign << std::endl;
#endif

if (components.exponent > detail::emax_v<decimal64_t>)
{
*this = from_bits(detail::d64_inf_mask);
}
else
{
*this = decimal64_t {components.mantissa, components.exponent, components.sign};
}
*this = decimal64_t {components.mantissa, components.exponent, components.sign};
}
}

Expand Down
7 changes: 6 additions & 1 deletion include/boost/decimal/decimal_fast128_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,12 @@ constexpr decimal_fast128_t::decimal_fast128_t(T1 coeff, T2 exp, const detail::c

const auto biased_exp {static_cast<int>(exp) + detail::bias_v<decimal_fast128_t>};

if (biased_exp > detail::max_biased_exp_v<decimal_fast128_t>)
if (biased_exp > detail::max_biased_exp_v<decimal_fast128_t> && detail::overflow_is_finite(is_negative))
{
significand_ = static_cast<significand_type>(max_normal_significand);
exponent_ = static_cast<exponent_type>(detail::max_biased_exp_v<decimal_fast128_t>);
}
else if (biased_exp > detail::max_biased_exp_v<decimal_fast128_t>)
{
significand_ = detail::d128_fast_inf;
}
Expand Down
7 changes: 6 additions & 1 deletion include/boost/decimal/decimal_fast32_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,12 @@ constexpr decimal_fast32_t::decimal_fast32_t(T1 coeff, T2 exp, const detail::con
const auto biased_exp {static_cast<int>(exp) + detail::bias};

// decimal32_t exponent holds 8 bits
if (biased_exp > detail::max_biased_exp_v<decimal_fast32_t>)
if (biased_exp > detail::max_biased_exp_v<decimal_fast32_t> && detail::overflow_is_finite(is_negative))
{
significand_ = static_cast<significand_type>(max_normal_significand);
exponent_ = static_cast<exponent_type>(detail::max_biased_exp_v<decimal_fast32_t>);
}
else if (biased_exp > detail::max_biased_exp_v<decimal_fast32_t>)
{
significand_ = detail::d32_fast_inf;
}
Expand Down
7 changes: 6 additions & 1 deletion include/boost/decimal/decimal_fast64_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,12 @@ constexpr decimal_fast64_t::decimal_fast64_t(T1 coeff, T2 exp, const detail::con

const auto biased_exp {static_cast<int>(exp) + detail::bias_v<decimal64_t>};

if (biased_exp > detail::max_biased_exp_v<decimal64_t>)
if (biased_exp > detail::max_biased_exp_v<decimal64_t> && detail::overflow_is_finite(is_negative))
{
significand_ = static_cast<significand_type>(max_normal_significand);
exponent_ = static_cast<exponent_type>(detail::max_biased_exp_v<decimal64_t>);
}
else if (biased_exp > detail::max_biased_exp_v<decimal64_t>)
{
significand_ = detail::d64_fast_inf;
}
Expand Down
15 changes: 15 additions & 0 deletions include/boost/decimal/detail/fenv_rounding.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,21 @@ BOOST_DECIMAL_CUDA_CONSTEXPR auto fenv_round(T& val, bool is_neg = false, bool s

#endif

// IEEE 754-2019 7.4: an overflow is the largest finite value in the toward zero mode, and
// in the directed mode which points at zero for the sign. Else it is an infinity.
BOOST_DECIMAL_CUDA_CONSTEXPR auto overflow_is_finite(const bool is_negative) noexcept -> bool
{
auto round {_boost_decimal_global_rounding_mode};
#ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION
if (!BOOST_DECIMAL_IS_CONSTANT_EVALUATED(is_negative))
{
round = _boost_decimal_global_runtime_rounding_mode;
}
#endif
return round == rounding_mode::fe_dec_toward_zero ||
round == (is_negative ? rounding_mode::fe_dec_upward : rounding_mode::fe_dec_downward);
}

#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wsign-conversion"
Expand Down
2 changes: 2 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ run github_issue_1451.cpp ;
run github_issue_1453.cpp ;
run github_issue_1455.cpp ;
run github_issue_1455_downward.cpp ;
run github_issue_1459.cpp ;
run github_issue_1459_toward_zero.cpp ;

run link_1.cpp link_2.cpp link_3.cpp ;
run quick.cpp ;
Expand Down
98 changes: 98 additions & 0 deletions test/github_issue_1459.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// https://github.com/boostorg/decimal/issues/1459
//
// IEEE 754-2019 7.4: an overflow is the largest finite value in the toward zero mode, and
// in the directed mode which points at zero for the sign. Every overflow gave an infinity.

#include <boost/decimal.hpp>
#include <boost/core/lightweight_test.hpp>
#include <limits>

using namespace boost::decimal;

// BOOST_DECIMAL_IF_CONSTEXPR is a plain if before C++17, so the constant condition warns
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#endif

// The largest finite value or an infinity, with the sign of the result
template <typename T>
void check_overflow(const T value, const bool neg, const bool finite)
{
BOOST_TEST_EQ(signbit(value), neg);
BOOST_TEST_EQ(isinf(value), !finite);
BOOST_TEST_EQ(abs(value) == (std::numeric_limits<T>::max)(), finite);
}

template <typename T>
void check(const rounding_mode mode)
{
fesetround(mode);
const T m {(std::numeric_limits<T>::max)()};
const T zero {0, 0};
const T two {2, 0};
const bool pos {mode == rounding_mode::fe_dec_toward_zero || mode == rounding_mode::fe_dec_downward};
const bool neg {mode == rounding_mode::fe_dec_toward_zero || mode == rounding_mode::fe_dec_upward};

check_overflow(m * m, false, pos);
check_overflow(-m * m, true, neg);
check_overflow(m + m, false, pos);
check_overflow(-m - m, true, neg);
check_overflow(m * two, false, pos);
check_overflow(-m * two, true, neg);
check_overflow(fma(m, m, zero), false, pos);
check_overflow(fma(-m, m, zero), true, neg);
check_overflow(T {1, 99999}, false, pos);
check_overflow(T {-1, 99999}, true, neg);
check_overflow(T {"1e99999"}, false, pos);
check_overflow(T {"-1e99999"}, true, neg);

// The largest long double overflows the 32 and 64 bit types, but not the 128 bit types
BOOST_DECIMAL_IF_CONSTEXPR (std::numeric_limits<long double>::max_exponent10 > std::numeric_limits<T>::max_exponent10)
{
check_overflow(T {std::numeric_limits<long double>::max()}, false, pos);
check_overflow(T {-std::numeric_limits<long double>::max()}, true, neg);
}
}

template <typename T>
void test()
{
#ifdef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION
// fesetround has no effect here, thus only the compile-time mode is tested
check<T>(_boost_decimal_global_rounding_mode);
#else
check<T>(rounding_mode::fe_dec_to_nearest);
check<T>(rounding_mode::fe_dec_downward);
check<T>(rounding_mode::fe_dec_upward);
check<T>(rounding_mode::fe_dec_toward_zero);
check<T>(rounding_mode::fe_dec_to_nearest_from_zero);
#endif
}

#ifdef _MSC_VER
#pragma warning(pop)
#endif

int main()
{
constexpr bool finite_positive {_boost_decimal_global_rounding_mode == rounding_mode::fe_dec_toward_zero ||
_boost_decimal_global_rounding_mode == rounding_mode::fe_dec_downward};
static_assert(isinf(decimal32_t {1, 99999}) != finite_positive, "");
static_assert(isinf(decimal128_t {1, 99999}) != finite_positive, "");

test<decimal32_t>();
test<decimal_fast32_t>();
test<decimal64_t>();
test<decimal_fast64_t>();
test<decimal128_t>();
test<decimal_fast128_t>();

fesetround(rounding_mode::fe_dec_to_nearest);

return boost::report_errors();
}
11 changes: 11 additions & 0 deletions test/github_issue_1459_toward_zero.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// The same checks under the compile-time toward zero mode, where both signs are finite,
// and without constant evaluation detection, where only that mode exists

#define BOOST_DECIMAL_FE_DEC_TOWARD_ZERO
#define BOOST_DECIMAL_NO_CONSTEVAL_DETECTION

#include "github_issue_1459.cpp"
Loading