Skip to content

Commit 418fd62

Browse files
authored
Merge pull request #1098 from cppalliance/1091
Fix construction of overflow/underflow values
2 parents 5341d06 + 61fb6cb commit 418fd62

File tree

6 files changed

+81
-12
lines changed

6 files changed

+81
-12
lines changed

include/boost/decimal/decimal128_t.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,7 @@ constexpr decimal128_t::decimal128_t(T1 coeff, T2 exp, bool sign) noexcept
779779
else
780780
{
781781
bits_ = exp < 0 ? zero : detail::d128_inf_mask;
782+
bits_.high |= sign ? detail::d128_sign_mask : UINT64_C(0);
782783
}
783784
}
784785
}

include/boost/decimal/decimal32_t.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,9 @@ constexpr decimal32_t::decimal32_t(T1 coeff, T2 exp, bool sign) noexcept // NOLI
700700
}
701701
else
702702
{
703+
// Reset the value and make sure to preserve the sign of 0/inf
703704
bits_ = exp < 0 ? UINT32_C(0) : detail::d32_inf_mask;
705+
bits_ |= sign ? detail::d32_sign_mask : UINT32_C(0);
704706
}
705707
}
706708
}

include/boost/decimal/decimal64_t.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,9 @@ constexpr decimal64_t::decimal64_t(T1 coeff, T2 exp, bool sign) noexcept
693693
}
694694
else
695695
{
696+
// Reset the value and make sure to preserve the sign of 0/inf
696697
bits_ = exp < 0 ? UINT64_C(0) : detail::d64_inf_mask;
698+
bits_ |= sign ? detail::d64_sign_mask : UINT64_C(0);
697699
}
698700
}
699701
}

include/boost/decimal/dpd_conversion.hpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -435,21 +435,26 @@ constexpr auto from_dpd_d32(const std::uint32_t dpd) noexcept
435435
static_assert(std::is_same<DecimalType, decimal32_t>::value || std::is_same<DecimalType, decimal_fast32_t>::value,
436436
"Target decimal type must be 32-bits");
437437

438+
const auto sign {(dpd & detail::d32_sign_mask) != 0};
439+
438440
// First we check for non-finite values
439441
// Since they are in the same initial format as BID it's easy to check with our existing masks
440442
if ((dpd & detail::d32_inf_mask) == detail::d32_inf_mask)
441443
{
442444
if ((dpd & detail::d32_snan_mask) == detail::d32_snan_mask)
443445
{
444-
return std::numeric_limits<DecimalType>::signaling_NaN();
446+
return sign ? -std::numeric_limits<DecimalType>::signaling_NaN() :
447+
std::numeric_limits<DecimalType>::signaling_NaN();
445448
}
446449
else if ((dpd & detail::d32_nan_mask) == detail::d32_nan_mask)
447450
{
448-
return std::numeric_limits<DecimalType>::quiet_NaN();
451+
return sign ? -std::numeric_limits<DecimalType>::quiet_NaN() :
452+
std::numeric_limits<DecimalType>::quiet_NaN();
449453
}
450454
else
451455
{
452-
return std::numeric_limits<DecimalType>::infinity();
456+
return sign ? -std::numeric_limits<DecimalType>::infinity() :
457+
std::numeric_limits<DecimalType>::infinity();
453458
}
454459
}
455460

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

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

642+
const auto sign {(dpd & detail::d64_sign_mask) != 0};
643+
638644
// First we check for non-finite values
639645
// Since they are in the same initial format as BID it's easy to check with our existing masks
640646
if ((dpd & detail::d64_inf_mask) == detail::d64_inf_mask)
641647
{
642648
if ((dpd & detail::d64_snan_mask) == detail::d64_snan_mask)
643649
{
644-
return std::numeric_limits<DecimalType>::signaling_NaN();
650+
return sign ? -std::numeric_limits<DecimalType>::signaling_NaN() :
651+
std::numeric_limits<DecimalType>::signaling_NaN();
645652
}
646653
else if ((dpd & detail::d64_nan_mask) == detail::d64_nan_mask)
647654
{
648-
return std::numeric_limits<DecimalType>::quiet_NaN();
655+
return sign ? -std::numeric_limits<DecimalType>::quiet_NaN() :
656+
std::numeric_limits<DecimalType>::quiet_NaN();
649657
}
650658
else
651659
{
652-
return std::numeric_limits<DecimalType>::infinity();
660+
return sign ? -std::numeric_limits<DecimalType>::infinity() :
661+
std::numeric_limits<DecimalType>::infinity();
653662
}
654663
}
655664

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

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

852+
const auto sign {(dpd.high & detail::d128_sign_mask) != 0};
853+
844854
if ((dpd & detail::d128_inf_mask) == detail::d128_inf_mask)
845855
{
846856
if ((dpd & detail::d128_snan_mask) == detail::d128_snan_mask)
847857
{
848-
return std::numeric_limits<DecimalType>::signaling_NaN();
858+
return sign ? -std::numeric_limits<DecimalType>::signaling_NaN() :
859+
std::numeric_limits<DecimalType>::signaling_NaN();
849860
}
850861
else if ((dpd & detail::d128_nan_mask) == detail::d128_nan_mask)
851862
{
852-
return std::numeric_limits<DecimalType>::quiet_NaN();
863+
return sign ? -std::numeric_limits<DecimalType>::quiet_NaN() :
864+
std::numeric_limits<DecimalType>::quiet_NaN();
853865
}
854866
else
855867
{
856-
return std::numeric_limits<DecimalType>::infinity();
868+
return sign ? -std::numeric_limits<DecimalType>::infinity() :
869+
std::numeric_limits<DecimalType>::infinity();
857870
}
858871
}
859872

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

864877
// The bit lengths are the same as used in the standard bid format
865-
const auto sign {(dpd.high & detail::d128_sign_mask) != 0};
866878
const auto combination_field_bits {(dpd.high & d128_dpd_combination_field_mask_high_bits) >> 58U};
867879
const auto exponent_field_bits {(dpd.high & d128_dpd_exponent_mask_high_bits) >> 46U};
868880
auto significand_bits {(dpd & d128_dpd_significand_mask)};

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ run github_issue_1054.cpp ;
6767
run github_issue_1055.cpp ;
6868
run github_issue_1057.cpp ;
6969
compile-fail github_issue_1087.cpp ;
70+
run github_issue_1091.cpp ;
7071
run link_1.cpp link_2.cpp link_3.cpp ;
7172
run quick.cpp ;
7273
run random_decimal32_comp.cpp ;

test/github_issue_1091.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
//
5+
// See: https://github.com/cppalliance/decimal/issues/1091
6+
7+
#include <boost/decimal.hpp>
8+
#include <boost/core/lightweight_test.hpp>
9+
10+
using namespace boost::decimal;
11+
12+
template <typename T>
13+
void test()
14+
{
15+
constexpr T one {1u, 0};
16+
T low {std::numeric_limits<T>::lowest()};
17+
BOOST_TEST(!isinf(low));
18+
19+
low *= 2;
20+
21+
BOOST_TEST(low < one);
22+
BOOST_TEST(one > low);
23+
BOOST_TEST(isinf(low));
24+
25+
T high {std::numeric_limits<T>::max()};
26+
BOOST_TEST(!isinf(high));
27+
28+
high *= 2;
29+
30+
BOOST_TEST(high > one);
31+
BOOST_TEST(one < high);
32+
BOOST_TEST(isinf(high));
33+
34+
T min {-std::numeric_limits<T>::denorm_min()};
35+
36+
min /= 2;
37+
38+
BOOST_TEST(min < one);
39+
BOOST_TEST(min == (one - one));
40+
BOOST_TEST(signbit(min));
41+
}
42+
43+
int main()
44+
{
45+
test<decimal32_t>();
46+
test<decimal64_t>();
47+
test<decimal128_t>();
48+
49+
return boost::report_errors();
50+
}
51+

0 commit comments

Comments
 (0)