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
1 change: 1 addition & 0 deletions include/boost/decimal/decimal128_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,7 @@ constexpr decimal128_t::decimal128_t(T1 coeff, T2 exp, bool sign) noexcept
else
{
bits_ = exp < 0 ? zero : detail::d128_inf_mask;
bits_.high |= sign ? detail::d128_sign_mask : UINT64_C(0);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions include/boost/decimal/decimal32_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,9 @@ constexpr decimal32_t::decimal32_t(T1 coeff, T2 exp, bool sign) noexcept // NOLI
}
else
{
// Reset the value and make sure to preserve the sign of 0/inf
bits_ = exp < 0 ? UINT32_C(0) : detail::d32_inf_mask;
bits_ |= sign ? detail::d32_sign_mask : UINT32_C(0);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions include/boost/decimal/decimal64_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,9 @@ constexpr decimal64_t::decimal64_t(T1 coeff, T2 exp, bool sign) noexcept
}
else
{
// Reset the value and make sure to preserve the sign of 0/inf
bits_ = exp < 0 ? UINT64_C(0) : detail::d64_inf_mask;
bits_ |= sign ? detail::d64_sign_mask : UINT64_C(0);
}
}
}
Expand Down
36 changes: 24 additions & 12 deletions include/boost/decimal/dpd_conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,21 +435,26 @@ constexpr auto from_dpd_d32(const std::uint32_t dpd) noexcept
static_assert(std::is_same<DecimalType, decimal32_t>::value || std::is_same<DecimalType, decimal_fast32_t>::value,
"Target decimal type must be 32-bits");

const auto sign {(dpd & detail::d32_sign_mask) != 0};

// First we check for non-finite values
// Since they are in the same initial format as BID it's easy to check with our existing masks
if ((dpd & detail::d32_inf_mask) == detail::d32_inf_mask)
{
if ((dpd & detail::d32_snan_mask) == detail::d32_snan_mask)
{
return std::numeric_limits<DecimalType>::signaling_NaN();
return sign ? -std::numeric_limits<DecimalType>::signaling_NaN() :
std::numeric_limits<DecimalType>::signaling_NaN();
}
else if ((dpd & detail::d32_nan_mask) == detail::d32_nan_mask)
{
return std::numeric_limits<DecimalType>::quiet_NaN();
return sign ? -std::numeric_limits<DecimalType>::quiet_NaN() :
std::numeric_limits<DecimalType>::quiet_NaN();
}
else
{
return std::numeric_limits<DecimalType>::infinity();
return sign ? -std::numeric_limits<DecimalType>::infinity() :
std::numeric_limits<DecimalType>::infinity();
}
}

Expand All @@ -458,7 +463,6 @@ constexpr auto from_dpd_d32(const std::uint32_t dpd) noexcept
constexpr std::uint32_t dpd_d32_combination_mask {UINT32_C(0b0'11111'000000'0000000000'0000000000)};

// The bit lengths are the same as used in the standard bid format
const auto sign {(dpd & detail::d32_sign_mask) != 0};
const auto combination_field_bits {(dpd & dpd_d32_combination_mask) >> 26U};
const auto exponent_field_bits {(dpd & dpd_d32_exponent_mask) >> 20U};
const auto significand_bits {(dpd & dpd_d32_significand_mask)};
Expand Down Expand Up @@ -635,21 +639,26 @@ constexpr auto from_dpd_d64(const std::uint64_t dpd) noexcept
static_assert(std::is_same<DecimalType, decimal64_t>::value || std::is_same<DecimalType, decimal_fast64_t>::value,
"Target decimal type must be 64-bits");

const auto sign {(dpd & detail::d64_sign_mask) != 0};

// First we check for non-finite values
// Since they are in the same initial format as BID it's easy to check with our existing masks
if ((dpd & detail::d64_inf_mask) == detail::d64_inf_mask)
{
if ((dpd & detail::d64_snan_mask) == detail::d64_snan_mask)
{
return std::numeric_limits<DecimalType>::signaling_NaN();
return sign ? -std::numeric_limits<DecimalType>::signaling_NaN() :
std::numeric_limits<DecimalType>::signaling_NaN();
}
else if ((dpd & detail::d64_nan_mask) == detail::d64_nan_mask)
{
return std::numeric_limits<DecimalType>::quiet_NaN();
return sign ? -std::numeric_limits<DecimalType>::quiet_NaN() :
std::numeric_limits<DecimalType>::quiet_NaN();
}
else
{
return std::numeric_limits<DecimalType>::infinity();
return sign ? -std::numeric_limits<DecimalType>::infinity() :
std::numeric_limits<DecimalType>::infinity();
}
}

Expand All @@ -659,7 +668,6 @@ constexpr auto from_dpd_d64(const std::uint64_t dpd) noexcept
constexpr std::uint64_t dpd_d64_exponent_field_mask {UINT64_C(0b0'00000'11111111'0000000000'0000000000'0000000000'0000000000'0000000000)};
constexpr std::uint64_t dpd_d64_significand_field_mask {UINT64_C(0b0'00000'00000000'1111111111'1111111111'1111111111'1111111111'1111111111)};

const auto sign {(dpd & detail::d64_sign_mask) != 0};
const auto combination_field_bits {(dpd & dpd_d64_combination_field_mask) >> 58U};
const auto exponent_field_bits {(dpd & dpd_d64_exponent_field_mask) >> 50U};
auto significand_bits {(dpd & dpd_d64_significand_field_mask)};
Expand Down Expand Up @@ -841,19 +849,24 @@ constexpr auto from_dpd_d128(const int128::uint128_t dpd) noexcept
static_assert(std::is_same<DecimalType, decimal128_t>::value || std::is_same<DecimalType, decimal_fast128_t>::value,
"Target decimal type must be 128-bits");

const auto sign {(dpd.high & detail::d128_sign_mask) != 0};

if ((dpd & detail::d128_inf_mask) == detail::d128_inf_mask)
{
if ((dpd & detail::d128_snan_mask) == detail::d128_snan_mask)
{
return std::numeric_limits<DecimalType>::signaling_NaN();
return sign ? -std::numeric_limits<DecimalType>::signaling_NaN() :
std::numeric_limits<DecimalType>::signaling_NaN();
}
else if ((dpd & detail::d128_nan_mask) == detail::d128_nan_mask)
{
return std::numeric_limits<DecimalType>::quiet_NaN();
return sign ? -std::numeric_limits<DecimalType>::quiet_NaN() :
std::numeric_limits<DecimalType>::quiet_NaN();
}
else
{
return std::numeric_limits<DecimalType>::infinity();
return sign ? -std::numeric_limits<DecimalType>::infinity() :
std::numeric_limits<DecimalType>::infinity();
}
}

Expand All @@ -862,7 +875,6 @@ constexpr auto from_dpd_d128(const int128::uint128_t dpd) noexcept
constexpr int128::uint128_t d128_dpd_significand_mask {UINT64_C(0b1111111111'1111111111'1111111111'1111111111'111111), UINT64_MAX};

// The bit lengths are the same as used in the standard bid format
const auto sign {(dpd.high & detail::d128_sign_mask) != 0};
const auto combination_field_bits {(dpd.high & d128_dpd_combination_field_mask_high_bits) >> 58U};
const auto exponent_field_bits {(dpd.high & d128_dpd_exponent_mask_high_bits) >> 46U};
auto significand_bits {(dpd & d128_dpd_significand_mask)};
Expand Down
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ run github_issue_1054.cpp ;
run github_issue_1055.cpp ;
run github_issue_1057.cpp ;
compile-fail github_issue_1087.cpp ;
run github_issue_1091.cpp ;
run link_1.cpp link_2.cpp link_3.cpp ;
run quick.cpp ;
run random_decimal32_comp.cpp ;
Expand Down
51 changes: 51 additions & 0 deletions test/github_issue_1091.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2025 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/1091

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

using namespace boost::decimal;

template <typename T>
void test()
{
constexpr T one {1u, 0};
T low {std::numeric_limits<T>::lowest()};
BOOST_TEST(!isinf(low));

low *= 2;

BOOST_TEST(low < one);
BOOST_TEST(one > low);
BOOST_TEST(isinf(low));

T high {std::numeric_limits<T>::max()};
BOOST_TEST(!isinf(high));

high *= 2;

BOOST_TEST(high > one);
BOOST_TEST(one < high);
BOOST_TEST(isinf(high));

T min {-std::numeric_limits<T>::denorm_min()};

min /= 2;

BOOST_TEST(min < one);
BOOST_TEST(min == (one - one));
BOOST_TEST(signbit(min));
}

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

return boost::report_errors();
}