diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index e8c1b6780..6c3dec916 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -49,6 +49,7 @@ jobs: git submodule update --init libs/predef git submodule update --init libs/static_assert git submodule update --init libs/test + git submodule update --init libs/random ./bootstrap.sh ./b2 headers - name: gcc-gcov-native diff --git a/include/boost/decimal/decimal128.hpp b/include/boost/decimal/decimal128.hpp index 125dd58f5..b2a0dc60a 100644 --- a/include/boost/decimal/decimal128.hpp +++ b/include/boost/decimal/decimal128.hpp @@ -337,7 +337,10 @@ BOOST_DECIMAL_EXPORT class decimal128 final explicit constexpr operator std::bfloat16_t() const noexcept; #endif - template , bool> = true> + template && (detail::impl::decimal_val_v > detail::impl::decimal_val_v), bool> = true> + constexpr operator Decimal() const noexcept; + + template && (detail::impl::decimal_val_v <= detail::impl::decimal_val_v), bool> = true> explicit constexpr operator Decimal() const noexcept; // cmath functions that are easier as friends @@ -1112,7 +1115,13 @@ constexpr decimal128::operator std::bfloat16_t() const noexcept } #endif -template , bool>> +template && (detail::impl::decimal_val_v > detail::impl::decimal_val_v), bool>> +constexpr decimal128::operator Decimal() const noexcept +{ + return to_decimal(*this); +} + +template && (detail::impl::decimal_val_v <= detail::impl::decimal_val_v), bool>> constexpr decimal128::operator Decimal() const noexcept { return to_decimal(*this); diff --git a/include/boost/decimal/decimal32.hpp b/include/boost/decimal/decimal32.hpp index 55109730b..a10ff1178 100644 --- a/include/boost/decimal/decimal32.hpp +++ b/include/boost/decimal/decimal32.hpp @@ -273,8 +273,9 @@ BOOST_DECIMAL_EXPORT class decimal32 final // NOLINT(cppcoreguidelines-special-m explicit constexpr operator detail::uint128_t() const noexcept; #endif + // We allow implict promotions to and decimal type with greater or equal precision (e.g. decimal32_fast) template , bool> = true> - explicit constexpr operator Decimal() const noexcept; + constexpr operator Decimal() const noexcept; // 3.2.5 initialization from coefficient and exponent: #ifdef BOOST_DECIMAL_HAS_CONCEPTS diff --git a/include/boost/decimal/decimal32_fast.hpp b/include/boost/decimal/decimal32_fast.hpp index 5e8b34b92..0e4f4a19c 100644 --- a/include/boost/decimal/decimal32_fast.hpp +++ b/include/boost/decimal/decimal32_fast.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -112,7 +113,7 @@ BOOST_DECIMAL_EXPORT class decimal32_fast final BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint32_t); public: - constexpr decimal32_fast() noexcept {} + constexpr decimal32_fast() noexcept = default; template && detail::is_integral_v, bool> = true> constexpr decimal32_fast(T1 coeff, T2 exp, bool sign = false) noexcept; @@ -319,7 +320,10 @@ BOOST_DECIMAL_EXPORT class decimal32_fast final // Conversion to other decimal type - template , bool> = true> + template && (detail::impl::decimal_val_v > detail::impl::decimal_val_v), bool> = true> + constexpr operator Decimal() const noexcept; + + template && (detail::impl::decimal_val_v <= detail::impl::decimal_val_v), bool> = true> explicit constexpr operator Decimal() const noexcept; 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 } #endif -template , bool>> +template && (detail::impl::decimal_val_v > detail::impl::decimal_val_v), bool>> +constexpr decimal32_fast::operator Decimal() const noexcept +{ + return to_decimal(*this); +} + +template && (detail::impl::decimal_val_v <= detail::impl::decimal_val_v), bool>> constexpr decimal32_fast::operator Decimal() const noexcept { return to_decimal(*this); diff --git a/include/boost/decimal/decimal64.hpp b/include/boost/decimal/decimal64.hpp index 3440fb33a..6f484caa5 100644 --- a/include/boost/decimal/decimal64.hpp +++ b/include/boost/decimal/decimal64.hpp @@ -294,7 +294,11 @@ BOOST_DECIMAL_EXPORT class decimal64 final #endif - template , bool> = true> + // Conversion to other decimal type + template && (detail::impl::decimal_val_v > detail::impl::decimal_val_v), bool> = true> + constexpr operator Decimal() const noexcept; + + template && (detail::impl::decimal_val_v <= detail::impl::decimal_val_v), bool> = true> explicit constexpr operator Decimal() const noexcept; // 3.2.6 Conversion to floating-point type @@ -951,8 +955,13 @@ constexpr decimal64::operator detail::uint128_t() const noexcept #endif +template && (detail::impl::decimal_val_v > detail::impl::decimal_val_v), bool>> +constexpr decimal64::operator Decimal() const noexcept +{ + return to_decimal(*this); +} -template , bool>> +template && (detail::impl::decimal_val_v <= detail::impl::decimal_val_v), bool>> constexpr decimal64::operator Decimal() const noexcept { return to_decimal(*this); diff --git a/include/boost/decimal/decimal64_fast.hpp b/include/boost/decimal/decimal64_fast.hpp index f15a278a5..4b7d99fe2 100644 --- a/include/boost/decimal/decimal64_fast.hpp +++ b/include/boost/decimal/decimal64_fast.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -267,7 +268,11 @@ BOOST_DECIMAL_EXPORT class decimal64_fast final explicit constexpr operator std::bfloat16_t() const noexcept; #endif - template , bool> = true> + // Conversion to other decimal type + template && (detail::impl::decimal_val_v > detail::impl::decimal_val_v), bool> = true> + constexpr operator Decimal() const noexcept; + + template && (detail::impl::decimal_val_v <= detail::impl::decimal_val_v), bool> = true> explicit constexpr operator Decimal() const noexcept; // Unary Operators @@ -930,7 +935,13 @@ constexpr decimal64_fast::operator std::bfloat16_t() const noexcept } #endif -template , bool>> +template && (detail::impl::decimal_val_v > detail::impl::decimal_val_v), bool>> +constexpr decimal64_fast::operator Decimal() const noexcept +{ + return to_decimal(*this); +} + +template && (detail::impl::decimal_val_v <= detail::impl::decimal_val_v), bool>> constexpr decimal64_fast::operator Decimal() const noexcept { return to_decimal(*this); diff --git a/test/Jamfile b/test/Jamfile index d847de99b..82c3ad2a0 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -122,8 +122,14 @@ run test_git_issue_266.cpp ; run test_git_issue_271.cpp ; run test_hash.cpp ; run test_hermite.cpp ; +compile-fail test_illegal_decimal32_fast_implicit_conversions.cpp ; +compile-fail test_illegal_decimal32_implicit_conversions.cpp ; +compile-fail test_illegal_decimal64_fast_implicit_conversions.cpp ; +compile-fail test_illegal_decimal64_implicit_conversions.cpp ; +compile-fail test_illegal_decimal128_implicit_conversions.cpp ; run test_implicit_integral_conversion.cpp ; run test_laguerre.cpp ; +run test_legal_implicit_conversions.cpp ; run test_legendre.cpp ; run test_literals.cpp ; run test_limits.cpp ; diff --git a/test/cover/make_gcov_02_files.gmk b/test/cover/make_gcov_02_files.gmk index 36213b9b0..58cc7a2d9 100644 --- a/test/cover/make_gcov_02_files.gmk +++ b/test/cover/make_gcov_02_files.gmk @@ -8,13 +8,19 @@ FILES_PRJ := $(basename $(wildcard $(PATH_SRC)/*.cpp)) -FILES_EXCLUDE := $(PATH_SRC)/concepts_test.cpp \ - $(PATH_SRC)/link_1.cpp \ - $(PATH_SRC)/link_2.cpp \ - $(PATH_SRC)/link_3.cpp \ - $(PATH_SRC)/test_bad_evaluation_method.cpp \ - $(PATH_SRC)/test_explicit_floats.cpp \ - $(PATH_SRC)/test_from_chars.cpp +FILES_EXCLUDE := $(PATH_SRC)/concepts_test.cpp \ + $(PATH_SRC)/link_1.cpp \ + $(PATH_SRC)/link_2.cpp \ + $(PATH_SRC)/link_3.cpp \ + $(PATH_SRC)/test_bad_evaluation_method.cpp \ + $(PATH_SRC)/test_explicit_floats.cpp \ + $(PATH_SRC)/test_from_chars.cpp \ + $(PATH_SRC)/test_illegal_decimal32_fast_implicit_conversions.cpp \ + $(PATH_SRC)/test_illegal_decimal32_implicit_conversions.cpp \ + $(PATH_SRC)/test_illegal_decimal64_fast_implicit_conversions.cpp \ + $(PATH_SRC)/test_illegal_decimal64_implicit_conversions.cpp \ + $(PATH_SRC)/test_illegal_decimal128_implicit_conversions.cpp \ + FILES_EXCLUDE := $(basename $(FILES_EXCLUDE)) diff --git a/test/test_illegal_decimal128_implicit_conversions.cpp b/test/test_illegal_decimal128_implicit_conversions.cpp new file mode 100644 index 000000000..508a50f89 --- /dev/null +++ b/test/test_illegal_decimal128_implicit_conversions.cpp @@ -0,0 +1,22 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +template +void test_implicit() +{ + const From from_val {2, 1}; + const To to_val = from_val; + + BOOST_TEST_EQ(from_val, to_val); +} + +int main() +{ + test_implicit(); + + return boost::report_errors(); +} diff --git a/test/test_illegal_decimal32_fast_implicit_conversions.cpp b/test/test_illegal_decimal32_fast_implicit_conversions.cpp new file mode 100644 index 000000000..a33db8cb1 --- /dev/null +++ b/test/test_illegal_decimal32_fast_implicit_conversions.cpp @@ -0,0 +1,25 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +template +void test_implicit() +{ + const From from_val {2, 1}; + const To to_val = from_val; + + BOOST_TEST_EQ(from_val, to_val); +} + +int main() +{ + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + + return boost::report_errors(); +} diff --git a/test/test_illegal_decimal32_implicit_conversions.cpp b/test/test_illegal_decimal32_implicit_conversions.cpp new file mode 100644 index 000000000..bc33dd1e1 --- /dev/null +++ b/test/test_illegal_decimal32_implicit_conversions.cpp @@ -0,0 +1,26 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +template +void test_implicit() +{ + const From from_val {2, 1}; + const To to_val = from_val; + + BOOST_TEST_EQ(from_val, to_val); +} + +int main() +{ + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + + return boost::report_errors(); +} diff --git a/test/test_illegal_decimal64_fast_implicit_conversions.cpp b/test/test_illegal_decimal64_fast_implicit_conversions.cpp new file mode 100644 index 000000000..dda8a2a0c --- /dev/null +++ b/test/test_illegal_decimal64_fast_implicit_conversions.cpp @@ -0,0 +1,23 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +template +void test_implicit() +{ + const From from_val {2, 1}; + const To to_val = from_val; + + BOOST_TEST_EQ(from_val, to_val); +} + +int main() +{ + test_implicit(); + test_implicit(); + + return boost::report_errors(); +} diff --git a/test/test_illegal_decimal64_implicit_conversions.cpp b/test/test_illegal_decimal64_implicit_conversions.cpp new file mode 100644 index 000000000..0c12c51bf --- /dev/null +++ b/test/test_illegal_decimal64_implicit_conversions.cpp @@ -0,0 +1,24 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +template +void test_implicit() +{ + const From from_val {2, 1}; + const To to_val = from_val; + + BOOST_TEST_EQ(from_val, to_val); +} + +int main() +{ + test_implicit(); + test_implicit(); + test_implicit(); + + return boost::report_errors(); +} diff --git a/test/test_legal_implicit_conversions.cpp b/test/test_legal_implicit_conversions.cpp new file mode 100644 index 000000000..0a7292ca7 --- /dev/null +++ b/test/test_legal_implicit_conversions.cpp @@ -0,0 +1,84 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +template +void test_implicit() +{ + const From from_val {2, 1}; + const To to_val = from_val; + + BOOST_TEST_EQ(from_val, to_val); +} + +template +void test_explicit() +{ + const From from_val {2, 1}; + const To to_val = static_cast(from_val); + + BOOST_TEST_EQ(from_val, to_val); +} + +int main() +{ + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + test_explicit(); + + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + + test_implicit(); + test_implicit(); + test_implicit(); + test_implicit(); + + test_implicit(); + test_implicit(); + test_implicit(); + + test_implicit(); + test_implicit(); + + test_implicit(); + + return boost::report_errors(); +}