Skip to content

Commit 204d0eb

Browse files
authored
Merge pull request #1256 from cppalliance/downward
2 parents 7a727a7 + 953271a commit 204d0eb

File tree

3 files changed

+187
-10
lines changed

3 files changed

+187
-10
lines changed

include/boost/decimal/detail/add_impl.hpp

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ constexpr auto add_impl(const T& lhs, const T& rhs) noexcept -> ReturnType
3838
promoted_sig_type big_lhs {lhs.full_significand()};
3939
promoted_sig_type big_rhs {rhs.full_significand()};
4040
auto lhs_exp {lhs.biased_exponent()};
41-
const auto rhs_exp {rhs.biased_exponent()};
41+
auto rhs_exp {rhs.biased_exponent()};
4242

4343
// Align to larger exponent
4444
if (lhs_exp != rhs_exp)
@@ -69,12 +69,49 @@ constexpr auto add_impl(const T& lhs, const T& rhs) noexcept -> ReturnType
6969
{
7070
// If we are subtracting even disparate numbers we need to round down
7171
// E.g. "5e+95"_DF - "4e-100"_DF == "4.999999e+95"_DF
72-
73-
using sig_type = typename T::significand_type;
74-
75-
return big_lhs != 0U && (lhs_exp > rhs_exp) ?
76-
ReturnType{lhs.full_significand() - static_cast<sig_type>(lhs.isneg() != rhs.isneg()), lhs.biased_exponent(), lhs.isneg()} :
77-
ReturnType{rhs.full_significand() - static_cast<sig_type>(lhs.isneg() != rhs.isneg()), rhs.biased_exponent(), rhs.isneg()};
72+
const auto use_lhs {big_lhs != 0U && (lhs_exp > rhs_exp)};
73+
74+
// Need to check for the case where we have 1e+95 - anything = 9.99999... without losing a nine
75+
if (use_lhs)
76+
{
77+
if (big_rhs != 0U && (lhs.isneg() != rhs.isneg()))
78+
{
79+
const auto removed_zeros {detail::remove_trailing_zeros(big_lhs)};
80+
if (removed_zeros.trimmed_number == 1U)
81+
{
82+
--big_lhs;
83+
big_lhs *= 10U;
84+
big_lhs += 9U;
85+
--lhs_exp;
86+
}
87+
else
88+
{
89+
--big_lhs;
90+
}
91+
}
92+
93+
return ReturnType{big_lhs, lhs_exp, lhs.isneg()};
94+
}
95+
else
96+
{
97+
if (big_lhs != 0U && (lhs.isneg() != rhs.isneg()))
98+
{
99+
const auto removed_zeros {detail::remove_trailing_zeros(big_rhs)};
100+
if (removed_zeros.trimmed_number == 1U)
101+
{
102+
--big_rhs;
103+
big_rhs *= 10U;
104+
big_rhs += 9U;
105+
--rhs_exp;
106+
}
107+
else
108+
{
109+
--big_rhs;
110+
}
111+
}
112+
113+
return ReturnType{big_rhs, rhs_exp, rhs.isneg()};
114+
}
78115
}
79116
else
80117
{
@@ -150,10 +187,49 @@ constexpr auto d128_add_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
150187
{
151188
// If we are subtracting even disparate numbers we need to round down
152189
// E.g. "5e+95"_DF - "4e-100"_DF == "4.999999e+95"_DF
190+
const auto use_lhs {lhs_sig != 0U && (lhs_exp > rhs_exp)};
153191

154-
return lhs_sig != 0U && (lhs_exp > rhs_exp) ?
155-
ReturnType{lhs_sig - static_cast<T>(lhs_sign != rhs_sign), lhs_exp, lhs_sign} :
156-
ReturnType{rhs_sig - static_cast<T>(lhs_sign != rhs_sign), rhs_exp, rhs_sign};
192+
// Need to check for the case where we have 1e+95 - anything = 9.99999... without losing a nine
193+
if (use_lhs)
194+
{
195+
if (rhs_sig != 0U && (lhs_sign != rhs_sign))
196+
{
197+
const auto removed_zeros {detail::remove_trailing_zeros(lhs_sig)};
198+
if (removed_zeros.trimmed_number == 1U)
199+
{
200+
--lhs_sig;
201+
lhs_sig *= 10U;
202+
lhs_sig += 9U;
203+
--lhs_exp;
204+
}
205+
else
206+
{
207+
--lhs_sig;
208+
}
209+
}
210+
211+
return ReturnType{lhs_sig, lhs_exp, lhs_sign};
212+
}
213+
else
214+
{
215+
if (lhs_sig != 0U && (lhs_sign != rhs_sign))
216+
{
217+
const auto removed_zeros {detail::remove_trailing_zeros(rhs_sig)};
218+
if (removed_zeros.trimmed_number == 1U)
219+
{
220+
--rhs_sig;
221+
rhs_sig *= 10U;
222+
rhs_sig += 9U;
223+
--rhs_exp;
224+
}
225+
else
226+
{
227+
--rhs_sig;
228+
}
229+
}
230+
231+
return ReturnType{rhs_sig, rhs_exp, rhs_sign};
232+
}
157233
}
158234
else
159235
{

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ run test_decimal64_fast_stream.cpp ;
128128
run test_decimal64_stream.cpp ;
129129
#run test_decimal128_basis.cpp ;
130130
run test_decimal_quantum.cpp ;
131+
run test_downward_rounding.cpp ;
131132
run test_dpd_conversions.cpp ;
132133
run test_edges_and_behave.cpp ;
133134
run test_edit_members.cpp ;

test/test_downward_rounding.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
// Use compile-time rounding mode change so the test run on all platforms
6+
#define BOOST_DECIMAL_FE_DEC_DOWNWARD
7+
8+
#include <boost/decimal.hpp>
9+
#include <boost/core/lightweight_test.hpp>
10+
#include <functional>
11+
#include <iostream>
12+
#include <iomanip>
13+
14+
template <typename T, typename Func>
15+
void test(const char* lhs_str, const char* rhs_str, const char* result_str, Func f)
16+
{
17+
const T lhs {lhs_str};
18+
const T rhs {rhs_str};
19+
const T result {result_str};
20+
21+
const T func_result {f(lhs, rhs)};
22+
23+
BOOST_TEST_EQ(func_result, result);
24+
}
25+
26+
template <typename T>
27+
void test_add(const char* lhs, const char* rhs, const char* result)
28+
{
29+
std::cerr << std::setprecision(std::numeric_limits<T>::max_digits10);
30+
test<T>(lhs, rhs, result, std::plus<>());
31+
test<T>(rhs, lhs, result, std::plus<>());
32+
}
33+
34+
template <typename T>
35+
void test_sub(const char* lhs, const char* rhs, const char* result)
36+
{
37+
std::cerr << std::setprecision(std::numeric_limits<T>::max_digits10);
38+
test<T>(lhs, rhs, result, std::minus<>());
39+
}
40+
41+
int main()
42+
{
43+
using namespace boost::decimal;
44+
45+
test_add<decimal64_t>("1e+2", "-1e-383", "99.99999999999999");
46+
test_add<decimal64_t>("1e+1", "-1e-383", "9.999999999999999");
47+
48+
test_add<decimal_fast64_t>("1e+2", "-1e-383", "99.99999999999999");
49+
test_add<decimal_fast64_t>("1e+1", "-1e-383", "9.999999999999999");
50+
51+
test_sub<decimal64_t>("1e+2", "1e-383", "99.99999999999999");
52+
test_sub<decimal64_t>("1e+1", "1e-383", "9.999999999999999");
53+
54+
test_sub<decimal_fast64_t>("1e+2", "1e-383", "99.99999999999999");
55+
test_sub<decimal_fast64_t>("1e+1", "1e-383", "9.999999999999999");
56+
57+
test_add<decimal32_t>("1e+2", "-1e-20", "99.99999");
58+
test_add<decimal32_t>("1e+1", "-1e-20", "9.999999");
59+
60+
test_add<decimal_fast32_t>("1e+2", "-1e-20", "99.99999");
61+
test_add<decimal_fast32_t>("1e+1", "-1e-20", "9.999999");
62+
63+
test_sub<decimal32_t>("1e+2", "1e-20", "99.99999");
64+
test_sub<decimal32_t>("1e+1", "1e-20", "9.999999");
65+
66+
test_sub<decimal_fast32_t>("1e+2", "1e-20", "99.99999");
67+
test_sub<decimal_fast32_t>("1e+1", "1e-20", "9.999999");
68+
69+
test_add<decimal128_t>("1e+2", "-1e-383", "99.99999999999999999999999999999999");
70+
test_add<decimal128_t>("1e+1", "-1e-383", "9.999999999999999999999999999999999");
71+
72+
test_add<decimal_fast128_t>("1e+2", "-1e-383", "99.99999999999999999999999999999999");
73+
test_add<decimal_fast128_t>("1e+1", "-1e-383", "9.999999999999999999999999999999999");
74+
75+
test_sub<decimal128_t>("1e+2", "1e-383", "99.99999999999999999999999999999999");
76+
test_sub<decimal128_t>("1e+1", "1e-383", "9.999999999999999999999999999999999");
77+
78+
test_sub<decimal_fast128_t>("1e+2", "1e-383", "99.99999999999999999999999999999999");
79+
test_sub<decimal_fast128_t>("1e+1", "1e-383", "9.999999999999999999999999999999999");
80+
81+
test_add<decimal32_t>("9.999999e20", "0", "9.999999e20");
82+
test_add<decimal_fast32_t>("9.999999e20", "0", "9.999999e20");
83+
84+
test_sub<decimal32_t>("9.999999e20", "0", "9.999999e20");
85+
test_sub<decimal_fast32_t>("9.999999e20", "0", "9.999999e20");
86+
87+
test_add<decimal64_t>("9.999999999999999e200", "0", "9.999999999999999e200");
88+
test_add<decimal_fast64_t>("9.999999999999999e200", "0", "9.999999999999999e200");
89+
90+
test_sub<decimal64_t>("9.999999999999999e200", "0", "9.999999999999999e200");
91+
test_sub<decimal_fast64_t>("9.999999999999999e200", "0", "9.999999999999999e200");
92+
93+
test_add<decimal128_t>("9.999999999999999e200", "0", "9.999999999999999e200");
94+
test_add<decimal_fast128_t>("9.999999999999999e200", "0", "9.999999999999999e200");
95+
96+
test_sub<decimal128_t>("9.999999999999999e200", "0", "9.999999999999999e200");
97+
test_sub<decimal_fast128_t>("9.999999999999999e200", "0", "9.999999999999999e200");
98+
99+
return boost::report_errors();
100+
}

0 commit comments

Comments
 (0)