Skip to content

Commit 2a17f6e

Browse files
authored
Merge pull request #711 from cppalliance/no_normal
decimal32 operator< without normalization in the general case
2 parents 43c44e6 + e727f04 commit 2a17f6e

File tree

4 files changed

+164
-10
lines changed

4 files changed

+164
-10
lines changed

include/boost/decimal/decimal32.hpp

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,23 +1144,42 @@ constexpr auto operator<(Integer lhs, decimal32 rhs) noexcept
11441144
constexpr auto operator<=(decimal32 lhs, decimal32 rhs) noexcept -> bool
11451145
{
11461146
#ifndef BOOST_DECIMAL_FAST_MATH
1147-
if (isnan(lhs) || isnan(rhs))
1147+
if (!isfinite(lhs) || !isfinite(rhs))
11481148
{
1149-
return false;
1149+
if (isnan(lhs) || isnan(rhs))
1150+
{
1151+
return false;
1152+
}
1153+
if (isinf(lhs))
1154+
{
1155+
return signbit(lhs);
1156+
}
1157+
else if (isinf(rhs))
1158+
{
1159+
return !signbit(rhs);
1160+
}
11501161
}
11511162
#endif
11521163

1153-
return !(rhs < lhs);
1164+
return !less_parts_impl(rhs.full_significand(), rhs.biased_exponent(), rhs.isneg(),
1165+
lhs.full_significand(), lhs.biased_exponent(), lhs.isneg());
11541166
}
11551167

11561168
template <typename Integer>
11571169
constexpr auto operator<=(decimal32 lhs, Integer rhs) noexcept
11581170
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool)
11591171
{
11601172
#ifndef BOOST_DECIMAL_FAST_MATH
1161-
if (isnan(lhs))
1173+
if (!isfinite(lhs))
11621174
{
1163-
return false;
1175+
if (isnan(lhs))
1176+
{
1177+
return false;
1178+
}
1179+
else if (isinf(lhs))
1180+
{
1181+
return signbit(lhs);
1182+
}
11641183
}
11651184
#endif
11661185

@@ -1172,9 +1191,16 @@ constexpr auto operator<=(Integer lhs, decimal32 rhs) noexcept
11721191
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool)
11731192
{
11741193
#ifndef BOOST_DECIMAL_FAST_MATH
1175-
if (isnan(rhs))
1194+
if (!isfinite(rhs))
11761195
{
1177-
return false;
1196+
if (isnan(rhs))
1197+
{
1198+
return false;
1199+
}
1200+
else if (isinf(rhs))
1201+
{
1202+
return !signbit(rhs);
1203+
}
11781204
}
11791205
#endif
11801206

@@ -1183,7 +1209,26 @@ constexpr auto operator<=(Integer lhs, decimal32 rhs) noexcept
11831209

11841210
constexpr auto operator>(decimal32 lhs, decimal32 rhs) noexcept -> bool
11851211
{
1186-
return rhs < lhs;
1212+
#ifndef BOOST_DECIMAL_FAST_MATH
1213+
if (!isfinite(lhs) || !isfinite(rhs))
1214+
{
1215+
if (isnan(lhs) || isnan(rhs))
1216+
{
1217+
return false;
1218+
}
1219+
if (isinf(lhs))
1220+
{
1221+
return !signbit(lhs);
1222+
}
1223+
else if (isinf(rhs))
1224+
{
1225+
return signbit(rhs);
1226+
}
1227+
}
1228+
#endif
1229+
1230+
return less_parts_impl(rhs.full_significand(), rhs.biased_exponent(), rhs.isneg(),
1231+
lhs.full_significand(), lhs.biased_exponent(), lhs.isneg());
11871232
}
11881233

11891234
template <typename Integer>
@@ -1223,7 +1268,8 @@ constexpr auto operator>=(decimal32 lhs, decimal32 rhs) noexcept -> bool
12231268
}
12241269
#endif
12251270

1226-
return !(lhs < rhs);
1271+
return !less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(),
1272+
rhs.full_significand(), rhs.biased_exponent(), rhs.isneg());
12271273
}
12281274

12291275
template <typename Integer>

include/boost/decimal/detail/comparison.hpp

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,68 @@ constexpr auto operator!=(Decimal1 lhs, Decimal2 rhs) noexcept
170170
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType = decimal32, BOOST_DECIMAL_INTEGRAL T1,
171171
BOOST_DECIMAL_INTEGRAL U1, BOOST_DECIMAL_INTEGRAL T2, BOOST_DECIMAL_INTEGRAL U2>
172172
constexpr auto less_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
173-
T2 rhs_sig, U2 rhs_exp, bool rhs_sign) noexcept -> std::enable_if_t<std::is_same<DecimalType, decimal32>::value || std::is_same<DecimalType, decimal64>::value || std::is_same<DecimalType, decimal128>::value, bool>
173+
T2 rhs_sig, U2 rhs_exp, bool rhs_sign, bool normalized = false) noexcept -> std::enable_if_t<std::is_same<DecimalType, decimal32>::value, bool>
174+
{
175+
using comp_type = std::uint_fast64_t;
176+
177+
BOOST_DECIMAL_ASSERT(lhs_sig >= 0);
178+
BOOST_DECIMAL_ASSERT(rhs_sig >= 0);
179+
180+
if (lhs_sign != rhs_sign)
181+
{
182+
return lhs_sign;
183+
}
184+
185+
auto new_lhs_sig {static_cast<comp_type>(lhs_sig)};
186+
auto new_rhs_sig {static_cast<comp_type>(rhs_sig)};
187+
188+
if (new_lhs_sig == UINT64_C(0) || new_rhs_sig == UINT64_C(0))
189+
{
190+
return (new_lhs_sig == new_rhs_sig) ? false : (new_lhs_sig == 0 ? !rhs_sign : lhs_sign);
191+
}
192+
193+
const auto delta_exp {lhs_exp - rhs_exp};
194+
constexpr auto max_delta_diff {std::numeric_limits<std::uint_fast64_t>::digits10 - detail::precision_v<DecimalType>};
195+
196+
// If we can't do this correctly without normalization then do it and try again
197+
if (delta_exp > max_delta_diff || delta_exp < -max_delta_diff)
198+
{
199+
if (!normalized)
200+
{
201+
detail::normalize(lhs_sig, lhs_exp);
202+
detail::normalize(rhs_sig, rhs_exp);
203+
return less_parts_impl(lhs_sig, lhs_exp, lhs_sign,
204+
rhs_sig, rhs_exp, rhs_sign, true);
205+
}
206+
else
207+
{
208+
return rhs_sign ? rhs_exp < lhs_exp : rhs_exp > lhs_exp;
209+
}
210+
}
211+
212+
if (delta_exp >= 0)
213+
{
214+
new_lhs_sig *= detail::pow10(static_cast<comp_type>(delta_exp));
215+
lhs_exp -= delta_exp;
216+
}
217+
else
218+
{
219+
new_rhs_sig *= detail::pow10(static_cast<comp_type>(-delta_exp));
220+
rhs_exp += delta_exp;
221+
}
222+
223+
if (lhs_exp != rhs_exp)
224+
{
225+
return lhs_sign ? lhs_exp > rhs_exp : lhs_exp < rhs_exp;
226+
}
227+
228+
return lhs_sign ? new_lhs_sig > new_rhs_sig : new_lhs_sig < new_rhs_sig;
229+
}
230+
231+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType = decimal32, BOOST_DECIMAL_INTEGRAL T1,
232+
BOOST_DECIMAL_INTEGRAL U1, BOOST_DECIMAL_INTEGRAL T2, BOOST_DECIMAL_INTEGRAL U2>
233+
constexpr auto less_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
234+
T2 rhs_sig, U2 rhs_exp, bool rhs_sign) noexcept -> std::enable_if_t<std::is_same<DecimalType, decimal64>::value || std::is_same<DecimalType, decimal128>::value, bool>
174235
{
175236
using comp_type = std::conditional_t<std::is_same<DecimalType, decimal128>::value, detail::uint128, std::uint_fast64_t>;
176237

test/random_decimal32_comp.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,23 @@ void random_mixed_GE(T lower, T upper)
280280
BOOST_TEST(!(dist(rng) >= std::numeric_limits<decimal32>::quiet_NaN()));
281281
}
282282

283+
template <typename T>
284+
void spot_test_mixed_ge(T lhs, T rhs)
285+
{
286+
const decimal32 val1 {lhs};
287+
const T val2 {static_cast<T>(decimal32(rhs))};
288+
289+
if (!BOOST_TEST_EQ(val1 >= val2, lhs >= rhs))
290+
{
291+
// LCOV_EXCL_START
292+
std::cerr << " LHS: " << lhs
293+
<< "\nLHS D: " << val1
294+
<< "\n RHS: " << rhs
295+
<< "\nRHS D: " << val2 << std::endl;
296+
// LCOV_EXCL_STOP
297+
}
298+
}
299+
283300
template <typename T>
284301
void random_EQ(T lower, T upper)
285302
{
@@ -543,6 +560,8 @@ int main()
543560
random_mixed_GE(std::numeric_limits<long long>::min(), std::numeric_limits<long long>::max());
544561
random_mixed_GE(std::numeric_limits<unsigned long long>::min(), std::numeric_limits<unsigned long long>::max());
545562

563+
spot_test_mixed_ge(UINT64_C(15984034765439402622), UINT64_C(1366685175759710132));
564+
546565
random_EQ(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
547566
random_EQ(std::numeric_limits<unsigned>::min(), std::numeric_limits<unsigned>::max());
548567
random_EQ(std::numeric_limits<long>::min(), std::numeric_limits<long>::max());

test/random_decimal32_math.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,31 @@ void random_mixed_addition(T lower, T upper)
108108
BOOST_TEST(isnan(dist(rng) + std::numeric_limits<decimal32>::quiet_NaN()));
109109
}
110110

111+
template <typename T>
112+
void spot_random_mixed_addition(T lhs, T rhs)
113+
{
114+
const T val1 {lhs};
115+
const T val2 {rhs};
116+
117+
const decimal32 dec1 {val1};
118+
const T trunc_val_2 {static_cast<T>(decimal32(val2))};
119+
120+
const decimal32 res = dec1 + trunc_val_2;
121+
const auto res_int = static_cast<T>(res);
122+
123+
if (!BOOST_TEST_EQ(res_int, val1 + val2))
124+
{
125+
// LCOV_EXCL_START
126+
std::cerr << "Val 1: " << val1
127+
<< "\nDec 1: " << dec1
128+
<< "\nVal 2: " << val2
129+
<< "\nDec 2: " << trunc_val_2
130+
<< "\nDec res: " << res
131+
<< "\nInt res: " << val1 + val2 << std::endl;
132+
// LCOV_EXCL_STOP
133+
}
134+
}
135+
111136
template <typename T>
112137
void random_converted_addition(T lower, T upper)
113138
{
@@ -993,6 +1018,9 @@ int main()
9931018
random_right_shift();
9941019
random_mixed_right_shift();
9951020

1021+
spot_random_mixed_addition(-653573LL, 1391401LL);
1022+
spot_random_mixed_addition(894090LL, -1886315LL);
1023+
9961024
return boost::report_errors();
9971025
}
9981026

0 commit comments

Comments
 (0)