Skip to content

Commit 1feeee0

Browse files
authored
Merge pull request #816 from cppalliance/806
Allow implicit widening between decimal types and keep explicit narrowing
2 parents b4f8cad + a0ea406 commit 1feeee0

14 files changed

+274
-17
lines changed

.github/workflows/codecov.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jobs:
4949
git submodule update --init libs/predef
5050
git submodule update --init libs/static_assert
5151
git submodule update --init libs/test
52+
git submodule update --init libs/random
5253
./bootstrap.sh
5354
./b2 headers
5455
- name: gcc-gcov-native

include/boost/decimal/decimal128.hpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,10 @@ BOOST_DECIMAL_EXPORT class decimal128 final
337337
explicit constexpr operator std::bfloat16_t() const noexcept;
338338
#endif
339339

340-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool> = true>
340+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> > detail::impl::decimal_val_v<decimal128>), bool> = true>
341+
constexpr operator Decimal() const noexcept;
342+
343+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> <= detail::impl::decimal_val_v<decimal128>), bool> = true>
341344
explicit constexpr operator Decimal() const noexcept;
342345

343346
// cmath functions that are easier as friends
@@ -1112,7 +1115,13 @@ constexpr decimal128::operator std::bfloat16_t() const noexcept
11121115
}
11131116
#endif
11141117

1115-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool>>
1118+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> > detail::impl::decimal_val_v<decimal128>), bool>>
1119+
constexpr decimal128::operator Decimal() const noexcept
1120+
{
1121+
return to_decimal<Decimal>(*this);
1122+
}
1123+
1124+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> <= detail::impl::decimal_val_v<decimal128>), bool>>
11161125
constexpr decimal128::operator Decimal() const noexcept
11171126
{
11181127
return to_decimal<Decimal>(*this);

include/boost/decimal/decimal32.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,9 @@ BOOST_DECIMAL_EXPORT class decimal32 final // NOLINT(cppcoreguidelines-special-m
273273
explicit constexpr operator detail::uint128_t() const noexcept;
274274
#endif
275275

276+
// We allow implict promotions to and decimal type with greater or equal precision (e.g. decimal32_fast)
276277
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool> = true>
277-
explicit constexpr operator Decimal() const noexcept;
278+
constexpr operator Decimal() const noexcept;
278279

279280
// 3.2.5 initialization from coefficient and exponent:
280281
#ifdef BOOST_DECIMAL_HAS_CONCEPTS

include/boost/decimal/decimal32_fast.hpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <boost/decimal/detail/div_impl.hpp>
1717
#include <boost/decimal/detail/promote_significand.hpp>
1818
#include <boost/decimal/detail/ryu/ryu_generic_128.hpp>
19+
#include <boost/decimal/detail/promotion.hpp>
1920

2021
#ifndef BOOST_DECIMAL_BUILD_MODULE
2122
#include <limits>
@@ -112,7 +113,7 @@ BOOST_DECIMAL_EXPORT class decimal32_fast final
112113
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint32_t);
113114

114115
public:
115-
constexpr decimal32_fast() noexcept {}
116+
constexpr decimal32_fast() noexcept = default;
116117

117118
template <typename T1, typename T2, std::enable_if_t<detail::is_integral_v<T1> && detail::is_integral_v<T2>, bool> = true>
118119
constexpr decimal32_fast(T1 coeff, T2 exp, bool sign = false) noexcept;
@@ -319,7 +320,10 @@ BOOST_DECIMAL_EXPORT class decimal32_fast final
319320

320321

321322
// Conversion to other decimal type
322-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool> = true>
323+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> > detail::impl::decimal_val_v<decimal32_fast>), bool> = true>
324+
constexpr operator Decimal() const noexcept;
325+
326+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> <= detail::impl::decimal_val_v<decimal32_fast>), bool> = true>
323327
explicit constexpr operator Decimal() const noexcept;
324328

325329
friend constexpr auto direct_init(std::uint_fast32_t significand, std::uint_fast8_t exponent, bool sign) noexcept -> decimal32_fast;
@@ -1341,7 +1345,13 @@ constexpr decimal32_fast::operator std::bfloat16_t() const noexcept
13411345
}
13421346
#endif
13431347

1344-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool>>
1348+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> > detail::impl::decimal_val_v<decimal32_fast>), bool>>
1349+
constexpr decimal32_fast::operator Decimal() const noexcept
1350+
{
1351+
return to_decimal<Decimal>(*this);
1352+
}
1353+
1354+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> <= detail::impl::decimal_val_v<decimal32_fast>), bool>>
13451355
constexpr decimal32_fast::operator Decimal() const noexcept
13461356
{
13471357
return to_decimal<Decimal>(*this);

include/boost/decimal/decimal64.hpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,11 @@ BOOST_DECIMAL_EXPORT class decimal64 final
294294
#endif
295295

296296

297-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool> = true>
297+
// Conversion to other decimal type
298+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> > detail::impl::decimal_val_v<decimal64>), bool> = true>
299+
constexpr operator Decimal() const noexcept;
300+
301+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> <= detail::impl::decimal_val_v<decimal64>), bool> = true>
298302
explicit constexpr operator Decimal() const noexcept;
299303

300304
// 3.2.6 Conversion to floating-point type
@@ -951,8 +955,13 @@ constexpr decimal64::operator detail::uint128_t() const noexcept
951955

952956
#endif
953957

958+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> > detail::impl::decimal_val_v<decimal64>), bool>>
959+
constexpr decimal64::operator Decimal() const noexcept
960+
{
961+
return to_decimal<Decimal>(*this);
962+
}
954963

955-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool>>
964+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> <= detail::impl::decimal_val_v<decimal64>), bool>>
956965
constexpr decimal64::operator Decimal() const noexcept
957966
{
958967
return to_decimal<Decimal>(*this);

include/boost/decimal/decimal64_fast.hpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <boost/decimal/detail/div_impl.hpp>
1818
#include <boost/decimal/detail/promote_significand.hpp>
1919
#include <boost/decimal/detail/ryu/ryu_generic_128.hpp>
20+
#include <boost/decimal/detail/promotion.hpp>
2021

2122
#ifndef BOOST_DECIMAL_BUILD_MODULE
2223

@@ -267,7 +268,11 @@ BOOST_DECIMAL_EXPORT class decimal64_fast final
267268
explicit constexpr operator std::bfloat16_t() const noexcept;
268269
#endif
269270

270-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool> = true>
271+
// Conversion to other decimal type
272+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> > detail::impl::decimal_val_v<decimal64_fast>), bool> = true>
273+
constexpr operator Decimal() const noexcept;
274+
275+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> <= detail::impl::decimal_val_v<decimal64_fast>), bool> = true>
271276
explicit constexpr operator Decimal() const noexcept;
272277

273278
// Unary Operators
@@ -930,7 +935,13 @@ constexpr decimal64_fast::operator std::bfloat16_t() const noexcept
930935
}
931936
#endif
932937

933-
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal>, bool>>
938+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> > detail::impl::decimal_val_v<decimal64_fast>), bool>>
939+
constexpr decimal64_fast::operator Decimal() const noexcept
940+
{
941+
return to_decimal<Decimal>(*this);
942+
}
943+
944+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Decimal, std::enable_if_t<detail::is_decimal_floating_point_v<Decimal> && (detail::impl::decimal_val_v<Decimal> <= detail::impl::decimal_val_v<decimal64_fast>), bool>>
934945
constexpr decimal64_fast::operator Decimal() const noexcept
935946
{
936947
return to_decimal<Decimal>(*this);

test/Jamfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,14 @@ run test_git_issue_266.cpp ;
122122
run test_git_issue_271.cpp ;
123123
run test_hash.cpp ;
124124
run test_hermite.cpp ;
125+
compile-fail test_illegal_decimal32_fast_implicit_conversions.cpp ;
126+
compile-fail test_illegal_decimal32_implicit_conversions.cpp ;
127+
compile-fail test_illegal_decimal64_fast_implicit_conversions.cpp ;
128+
compile-fail test_illegal_decimal64_implicit_conversions.cpp ;
129+
compile-fail test_illegal_decimal128_implicit_conversions.cpp ;
125130
run test_implicit_integral_conversion.cpp ;
126131
run test_laguerre.cpp ;
132+
run test_legal_implicit_conversions.cpp ;
127133
run test_legendre.cpp ;
128134
run test_literals.cpp ;
129135
run test_limits.cpp ;

test/cover/make_gcov_02_files.gmk

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@
88

99
FILES_PRJ := $(basename $(wildcard $(PATH_SRC)/*.cpp))
1010

11-
FILES_EXCLUDE := $(PATH_SRC)/concepts_test.cpp \
12-
$(PATH_SRC)/link_1.cpp \
13-
$(PATH_SRC)/link_2.cpp \
14-
$(PATH_SRC)/link_3.cpp \
15-
$(PATH_SRC)/test_bad_evaluation_method.cpp \
16-
$(PATH_SRC)/test_explicit_floats.cpp \
17-
$(PATH_SRC)/test_from_chars.cpp
11+
FILES_EXCLUDE := $(PATH_SRC)/concepts_test.cpp \
12+
$(PATH_SRC)/link_1.cpp \
13+
$(PATH_SRC)/link_2.cpp \
14+
$(PATH_SRC)/link_3.cpp \
15+
$(PATH_SRC)/test_bad_evaluation_method.cpp \
16+
$(PATH_SRC)/test_explicit_floats.cpp \
17+
$(PATH_SRC)/test_from_chars.cpp \
18+
$(PATH_SRC)/test_illegal_decimal32_fast_implicit_conversions.cpp \
19+
$(PATH_SRC)/test_illegal_decimal32_implicit_conversions.cpp \
20+
$(PATH_SRC)/test_illegal_decimal64_fast_implicit_conversions.cpp \
21+
$(PATH_SRC)/test_illegal_decimal64_implicit_conversions.cpp \
22+
$(PATH_SRC)/test_illegal_decimal128_implicit_conversions.cpp \
23+
1824

1925
FILES_EXCLUDE := $(basename $(FILES_EXCLUDE))
2026

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
#include <boost/decimal.hpp>
6+
#include <boost/core/lightweight_test.hpp>
7+
8+
template <typename From, typename To>
9+
void test_implicit()
10+
{
11+
const From from_val {2, 1};
12+
const To to_val = from_val;
13+
14+
BOOST_TEST_EQ(from_val, to_val);
15+
}
16+
17+
int main()
18+
{
19+
test_implicit<boost::decimal::decimal128_fast, boost::decimal::decimal128>();
20+
21+
return boost::report_errors();
22+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
#include <boost/decimal.hpp>
6+
#include <boost/core/lightweight_test.hpp>
7+
8+
template <typename From, typename To>
9+
void test_implicit()
10+
{
11+
const From from_val {2, 1};
12+
const To to_val = from_val;
13+
14+
BOOST_TEST_EQ(from_val, to_val);
15+
}
16+
17+
int main()
18+
{
19+
test_implicit<boost::decimal::decimal64, boost::decimal::decimal32_fast>();
20+
test_implicit<boost::decimal::decimal64_fast, boost::decimal::decimal32_fast>();
21+
test_implicit<boost::decimal::decimal128, boost::decimal::decimal32_fast>();
22+
test_implicit<boost::decimal::decimal128_fast, boost::decimal::decimal32_fast>();
23+
24+
return boost::report_errors();
25+
}

0 commit comments

Comments
 (0)