Skip to content

Commit e083c2a

Browse files
authored
Merge pull request #1132 from cppalliance/1106
Fix `decimal_fast32_t` escaping it's domain in mul and div
2 parents 940c360 + 4fc844c commit e083c2a

File tree

4 files changed

+65
-64
lines changed

4 files changed

+65
-64
lines changed

include/boost/decimal/decimal_fast32_t.hpp

Lines changed: 11 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ BOOST_DECIMAL_EXPORT class decimal_fast32_t final
121121
template <typename ReturnType, typename T>
122122
friend constexpr auto detail::add_impl(const T& lhs, const T& rhs) noexcept -> ReturnType;
123123

124+
template <typename ReturnType, typename T>
125+
friend constexpr auto detail::mul_impl(const T& lhs, const T& rhs) noexcept -> ReturnType;
126+
124127
template <BOOST_DECIMAL_FAST_DECIMAL_FLOATING_TYPE DecimalType>
125128
BOOST_DECIMAL_FORCE_INLINE friend constexpr auto fast_equality_impl(const DecimalType& lhs, const DecimalType& rhs) noexcept -> bool;
126129

@@ -144,6 +147,9 @@ BOOST_DECIMAL_EXPORT class decimal_fast32_t final
144147
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
145148
friend constexpr auto detail::to_chars_hex_impl(char* first, char* last, const TargetDecimalType& value) noexcept -> to_chars_result;
146149

150+
template <typename DecimalType, typename T>
151+
friend constexpr auto detail::generic_div_impl(const T& lhs, const T& rhs) noexcept -> DecimalType;
152+
147153
public:
148154
constexpr decimal_fast32_t() noexcept = default;
149155

@@ -413,7 +419,7 @@ constexpr decimal_fast32_t::decimal_fast32_t(T1 coeff, T2 exp, bool sign) noexce
413419
sign_ = sign;
414420

415421
// Normalize in the constructor, so we never have to worry about it again
416-
detail::normalize<decimal32_t>(min_coeff, exp, sign);
422+
detail::normalize<decimal_fast32_t>(min_coeff, exp, sign);
417423

418424
significand_ = static_cast<significand_type>(min_coeff);
419425

@@ -902,26 +908,7 @@ constexpr auto operator*(const decimal_fast32_t lhs, const decimal_fast32_t rhs)
902908
}
903909
#endif
904910

905-
using mul_type = std::uint_fast64_t;
906-
907-
const auto isneg {lhs.sign_ != rhs.sign_};
908-
constexpr auto ten_pow_seven {detail::pow10(static_cast<mul_type>(6))};
909-
constexpr auto ten_pow_seven_exp_offset {95};
910-
constexpr auto ten_pow_six {detail::pow10(static_cast<mul_type>(5))};
911-
constexpr auto ten_pow_six_exp_offset {96};
912-
913-
auto res_sig {(static_cast<mul_type>(lhs.significand_) * static_cast<mul_type>(rhs.significand_))};
914-
const bool res_sig_14_dig {res_sig > UINT64_C(10000000000000)};
915-
res_sig /= res_sig_14_dig ? ten_pow_seven : ten_pow_six;
916-
auto res_exp {lhs.exponent_ + rhs.exponent_};
917-
res_exp -= res_sig_14_dig ? ten_pow_seven_exp_offset : ten_pow_six_exp_offset;
918-
919-
res_exp += detail::fenv_round<decimal_fast32_t>(res_sig, isneg);
920-
921-
BOOST_DECIMAL_ASSERT(res_sig >= 1'000'000 || res_sig == 0U);
922-
BOOST_DECIMAL_ASSERT(res_exp <= 9'999'999 || res_sig == 0U);
923-
924-
return direct_init(static_cast<decimal_fast32_t::significand_type>(res_sig), static_cast<decimal_fast32_t::exponent_type>(res_exp) , isneg);
911+
return detail::mul_impl<decimal_fast32_t>(lhs, rhs);
925912
}
926913

927914
template <typename Integer>
@@ -959,9 +946,9 @@ constexpr auto operator*(const Integer lhs, const decimal_fast32_t rhs) noexcept
959946

960947
constexpr auto div_impl(const decimal_fast32_t lhs, const decimal_fast32_t rhs, decimal_fast32_t& q, decimal_fast32_t& r) noexcept -> void
961948
{
962-
constexpr decimal_fast32_t zero {0, 0};
963-
964949
#ifndef BOOST_DECIMAL_FAST_MATH
950+
constexpr decimal_fast32_t zero {0, 0};
951+
965952
const bool sign {lhs.isneg() != rhs.isneg()};
966953
constexpr decimal_fast32_t nan {direct_init(detail::d32_fast_qnan, UINT8_C(0), false)};
967954
constexpr decimal_fast32_t inf {direct_init(detail::d32_fast_inf, UINT8_C(0), false)};
@@ -1007,43 +994,7 @@ constexpr auto div_impl(const decimal_fast32_t lhs, const decimal_fast32_t rhs,
1007994
static_cast<void>(r);
1008995
#endif
1009996

1010-
#ifdef BOOST_DECIMAL_DEBUG
1011-
std::cerr << "sig lhs: " << sig_lhs
1012-
<< "\nexp lhs: " << exp_lhs
1013-
<< "\nsig rhs: " << sig_rhs
1014-
<< "\nexp rhs: " << exp_rhs << std::endl;
1015-
#endif
1016-
1017-
using local_signed_exponent_type = std::common_type_t<std::int_fast32_t, int>;
1018-
1019-
static_assert(sizeof(local_signed_exponent_type) >= 4, "Error in local exponent type definition");
1020-
1021-
// We promote to uint64 since the significands are currently 32-bits
1022-
// By appending enough zeros to the LHS we end up finding what we need anyway
1023-
constexpr auto ten_pow_precision {detail::pow10(static_cast<std::uint_fast64_t>(detail::precision_v<decimal32_t>))};
1024-
const auto big_sig_lhs {static_cast<std::uint_fast64_t>(lhs.significand_) * ten_pow_precision};
1025-
auto res_sig {big_sig_lhs / static_cast<std::uint_fast64_t>(rhs.significand_)};
1026-
local_signed_exponent_type res_exp {static_cast<local_signed_exponent_type>(lhs.exponent_) - static_cast<local_signed_exponent_type>(rhs.exponent_) + 94};
1027-
const auto isneg {lhs.sign_ != rhs.sign_};
1028-
1029-
// If we have 8 figures round it down to 7
1030-
if (res_sig >= UINT64_C(10'000'000))
1031-
{
1032-
res_exp += detail::fenv_round<decimal_fast32_t>(res_sig, isneg);
1033-
}
1034-
1035-
BOOST_DECIMAL_ASSERT(res_sig >= 1'000'000 || res_sig == 0U);
1036-
BOOST_DECIMAL_ASSERT(res_exp <= 9'999'999 || res_sig == 0U);
1037-
1038-
if (BOOST_DECIMAL_LIKELY(res_exp >= 0))
1039-
{
1040-
q = direct_init(static_cast<decimal_fast32_t::significand_type>(res_sig), static_cast<decimal_fast32_t::exponent_type>(res_exp), isneg);
1041-
}
1042-
else
1043-
{
1044-
// Flush to zero
1045-
q = zero;
1046-
}
997+
q = detail::generic_div_impl<decimal_fast32_t>(lhs, rhs);
1047998
}
1048999

10491000
constexpr auto mod_impl(const decimal_fast32_t lhs, const decimal_fast32_t rhs, const decimal_fast32_t& q, decimal_fast32_t& r) noexcept -> void

include/boost/decimal/detail/div_impl.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto generic_div_impl(const T& lhs, const T
3232
constexpr auto precision_offset {std::numeric_limits<div_type>::digits10 - precision};
3333
constexpr auto ten_pow_offset {detail::pow10(static_cast<div_type>(precision_offset))};
3434

35-
const auto big_sig_lhs {lhs.sig * ten_pow_offset};
35+
const auto big_sig_lhs {lhs.full_significand() * ten_pow_offset};
3636

37-
const auto res_sig {big_sig_lhs / rhs.sig};
38-
const auto res_exp {(lhs.exp - precision_offset) - rhs.exp};
37+
const auto res_sig {big_sig_lhs / rhs.full_significand()};
38+
const auto res_exp {(lhs.biased_exponent() - precision_offset) - rhs.biased_exponent()};
3939

4040
// Normalizes sign handling
41-
bool sign {lhs.sign != rhs.sign};
41+
bool sign {lhs.isneg() != rhs.isneg()};
4242
if (BOOST_DECIMAL_UNLIKELY(res_sig == 0U))
4343
{
4444
sign = false;

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ run github_issue_1057.cpp ;
7070
compile-fail github_issue_1087.cpp ;
7171
run github_issue_1091.cpp ;
7272
run github_issue_1094.cpp ;
73+
run github_issue_1106.cpp ;
7374
run github_issue_1110.cpp ;
7475
run github_issue_1112.cpp ;
7576

test/github_issue_1106.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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/1094
6+
7+
#include <boost/decimal.hpp>
8+
#include <boost/core/lightweight_test.hpp>
9+
#include <limits>
10+
#include <cmath>
11+
12+
using namespace boost::decimal;
13+
14+
template <typename T>
15+
void test()
16+
{
17+
using std::isinf;
18+
using std::signbit;
19+
20+
const auto a {std::numeric_limits<T>::lowest() / 99};
21+
const T b {100};
22+
const auto c {a * b};
23+
24+
BOOST_TEST(isinf(c));
25+
BOOST_TEST(signbit(c));
26+
27+
// The same as above in principle
28+
const auto d {a / (1/b)};
29+
BOOST_TEST(isinf(d));
30+
BOOST_TEST(signbit(d));
31+
}
32+
33+
int main()
34+
{
35+
#ifndef _MSC_VER
36+
test<float>();
37+
test<double>();
38+
#endif
39+
40+
test<decimal32_t>();
41+
test<decimal64_t>();
42+
test<decimal128_t>();
43+
44+
test<decimal_fast32_t>();
45+
test<decimal_fast64_t>();
46+
test<decimal_fast128_t>();
47+
48+
return boost::report_errors();
49+
}

0 commit comments

Comments
 (0)