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: 17 additions & 0 deletions include/boost/decimal/decimal128_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,23 @@ constexpr decimal128_t::decimal128_t(T1 coeff, T2 exp, const detail::constructio
{
reduced_coeff *= detail::pow10(static_cast<significand_type>(biased_exp));
}
else if (biased_exp < 0)
{
const auto pos_biased_exp {-biased_exp};
bool sticky {false};
if (pos_biased_exp > 1)
{
// Need to ensure that we are following the current global rounding mode when packing subnormals
const auto shift_pow_10 {detail::pow10(static_cast<significand_type>(pos_biased_exp - 1))};
const auto div_res {detail::impl::divmod(reduced_coeff, shift_pow_10)};
reduced_coeff = div_res.quotient;
sticky = div_res.remainder != 0U;
}
// We may have to round the value so that it fits correctly
// e.g. 13e-399 -> 1e-398
detail::fenv_round<decimal128_t>(reduced_coeff, is_negative, sticky);
}

bits_ = reduced_coeff;
bits_.high |= is_negative ? detail::d128_sign_mask : UINT64_C(0); // Reset the sign bit
}
Expand Down
17 changes: 17 additions & 0 deletions include/boost/decimal/decimal32_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,23 @@ constexpr decimal32_t::decimal32_t(T1 coeff, T2 exp, const detail::construction_
{
reduced_coeff *= detail::pow10(static_cast<significand_type>(biased_exp));
}
else if (biased_exp < 0)
{
const auto pos_biased_exp {-biased_exp};
bool sticky {false};
if (pos_biased_exp > 1)
{
// Need to ensure that we are following the current global rounding mode when packing subnormals
const auto shift_pow_10 {detail::pow10(static_cast<significand_type>(pos_biased_exp - 1))};
const auto div_res {detail::impl::divmod(reduced_coeff, shift_pow_10)};
reduced_coeff = div_res.quotient;
sticky = div_res.remainder != 0U;
}
// We may have to round the value so that it fits correctly
// e.g. 13e-399 -> 1e-398
detail::fenv_round<decimal32_t>(reduced_coeff, is_negative, sticky);
}

bits_ |= reduced_coeff;
}
else if (digit_delta < 0 && coeff_digits - digit_delta <= detail::precision)
Expand Down
17 changes: 17 additions & 0 deletions include/boost/decimal/decimal64_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,23 @@ constexpr decimal64_t::decimal64_t(T1 coeff, T2 exp, const detail::construction_
{
reduced_coeff *= detail::pow10(static_cast<significand_type>(biased_exp));
}
else if (biased_exp < 0)
{
const auto pos_biased_exp {-biased_exp};
bool sticky {false};
if (pos_biased_exp > 1)
{
// Need to ensure that we are following the current global rounding mode when packing subnormals
const auto shift_pow_10 {detail::pow10(static_cast<significand_type>(pos_biased_exp - 1))};
const auto div_res {detail::impl::divmod(reduced_coeff, shift_pow_10)};
reduced_coeff = div_res.quotient;
sticky = div_res.remainder != 0U;
}
// We may have to round the value so that it fits correctly
// e.g. 13e-399 -> 1e-398
detail::fenv_round<decimal64_t>(reduced_coeff, is_negative, sticky);
}

bits_ |= reduced_coeff;
}
else if (digit_delta < 0 && coeff_digits - digit_delta <= detail::precision_v<decimal64_t>)
Expand Down
8 changes: 7 additions & 1 deletion include/boost/decimal/detail/cmath/next.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ constexpr auto nextafter_impl(const DecimalType val, const bool direction) noexc
sig = removed_zeros.trimmed_number;
exp += static_cast<int>(removed_zeros.number_of_removed_zeros);

if (removed_zeros.number_of_removed_zeros > 0)
if (exp == detail::etiny_v<DecimalType>)
{
// If we are at the absolute minimum just add to the sig
// e.g. 2e-101 < 1.1e-100
++sig;
}
else if (removed_zeros.number_of_removed_zeros > 0)
{
// We need to shift an add
// 1 -> 11 instead of 2 since 11e-101 < 2e-100 starting at 1e-100
Expand Down
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ run github_issue_1294.cpp ;
run github_issue_1299.cpp ;
run github_issue_1302.cpp ;
run github_issue_1304.cpp ;
run github_issue_1306.cpp ;

run link_1.cpp link_2.cpp link_3.cpp ;
run quick.cpp ;
Expand Down
2 changes: 1 addition & 1 deletion test/github_issue_1105.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void test_non_preserving()
BOOST_TEST_LE(two_val, next);

const auto nines_value = decimal32_t{"99e-101"};
const auto next_nines_res = decimal32_t{"991e-102"};
const auto next_nines_res = decimal32_t{"100e-101"};
const auto res = nextafter(nines_value, one);
BOOST_TEST_EQ(res, next_nines_res);

Expand Down
36 changes: 36 additions & 0 deletions test/github_issue_1306.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// See: https://github.com/cppalliance/decimal/issues/1306

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

using namespace boost::decimal;

template <typename T>
void test()
{
const T downward {13, detail::etiny_v<T> + 3};
BOOST_TEST_EQ(downward * T{"1e-4"}, std::numeric_limits<T>::denorm_min());

const T upward {15, detail::etiny_v<T> + 3};
BOOST_TEST_EQ(upward * T{"1e-4"}, T(2, detail::etiny_v<T>));

const T non_rounded {1234, detail::etiny_v<T> - 3};
BOOST_TEST_EQ(non_rounded, std::numeric_limits<T>::denorm_min());

const T rounded {1999, detail::etiny_v<T> - 3};
BOOST_TEST_EQ(rounded, T(2, detail::etiny_v<T>));
}

int main()
{
test<decimal32_t>();
test<decimal64_t>();
test<decimal128_t>();

return boost::report_errors();
}