From b82328cbc584b31031df3b9f4f99753acb1dbde6 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 19 Jan 2026 11:43:07 -0800 Subject: [PATCH 1/4] Add reproducer test set --- test/Jamfile | 1 + test/github_issue_1304.cpp | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 test/github_issue_1304.cpp diff --git a/test/Jamfile b/test/Jamfile index 1bc5282c5..2696639a1 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -85,6 +85,7 @@ run github_issue_1260.cpp ; run github_issue_1294.cpp ; run github_issue_1299.cpp ; run github_issue_1302.cpp ; +run github_issue_1304.cpp ; run link_1.cpp link_2.cpp link_3.cpp ; run quick.cpp ; diff --git a/test/github_issue_1304.cpp b/test/github_issue_1304.cpp new file mode 100644 index 000000000..dfbb01ad5 --- /dev/null +++ b/test/github_issue_1304.cpp @@ -0,0 +1,37 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// See: https://github.com/cppalliance/decimal/issues/1304 + +#include +#include +#include + +using namespace boost::decimal; + +template +void test() +{ + BOOST_TEST_EQ(std::numeric_limits::infinity() - T{1000}, std::numeric_limits::infinity()); + BOOST_TEST_EQ(std::numeric_limits::infinity() - 1000, std::numeric_limits::infinity()); + BOOST_TEST_EQ(-std::numeric_limits::infinity() - T{1000}, -std::numeric_limits::infinity()); + BOOST_TEST_EQ(-std::numeric_limits::infinity() - 1000, -std::numeric_limits::infinity()); + BOOST_TEST_EQ(-T{1000} - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(-1000 - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(T{1000} - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(1000 - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); +} + +int main() +{ + test(); + test(); + test(); + + test(); + test(); + test(); + + return boost::report_errors(); +} From 9dc6a5637820174a1a653106e879260c2a07f3b4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 19 Jan 2026 11:43:26 -0800 Subject: [PATCH 2/4] Fix subtraction of infinity returning positive inf --- include/boost/decimal/decimal128_t.hpp | 9 +++++++++ include/boost/decimal/decimal32_t.hpp | 9 +++++++++ include/boost/decimal/decimal64_t.hpp | 11 ++++++++++- include/boost/decimal/decimal_fast128_t.hpp | 9 +++++++++ include/boost/decimal/decimal_fast32_t.hpp | 9 +++++++++ include/boost/decimal/decimal_fast64_t.hpp | 9 +++++++++ 6 files changed, 55 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index dab83bd8d..b432d3b70 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1709,6 +1709,10 @@ constexpr auto operator-(const decimal128_t& lhs, const decimal128_t& rhs) noexc { return from_bits(detail::d128_nan_mask); } + if (isinf(rhs)) + { + return -rhs; + } return detail::check_non_finite(lhs, rhs); } @@ -1755,6 +1759,11 @@ constexpr auto operator-(const Integer lhs, const decimal128_t rhs) noexcept #ifndef BOOST_DECIMAL_FAST_MATH if (not_finite(rhs)) { + if (isinf(rhs)) + { + return -rhs; + } + return detail::check_non_finite(rhs); } #endif diff --git a/include/boost/decimal/decimal32_t.hpp b/include/boost/decimal/decimal32_t.hpp index c3e285db9..86c804ed1 100644 --- a/include/boost/decimal/decimal32_t.hpp +++ b/include/boost/decimal/decimal32_t.hpp @@ -1088,6 +1088,10 @@ constexpr auto operator-(const decimal32_t lhs, const decimal32_t rhs) noexcept { return from_bits(detail::d32_nan_mask); } + if (isinf(rhs)) + { + return -rhs; + } return detail::check_non_finite(lhs, rhs); } @@ -1144,6 +1148,11 @@ constexpr auto operator-(const Integer lhs, const decimal32_t rhs) noexcept #ifndef BOOST_DECIMAL_FAST_MATH if (!isfinite(rhs)) { + if (isinf(rhs)) + { + return -rhs; + } + return detail::check_non_finite(rhs); } #endif diff --git a/include/boost/decimal/decimal64_t.hpp b/include/boost/decimal/decimal64_t.hpp index e789b8bad..7eebd1dea 100644 --- a/include/boost/decimal/decimal64_t.hpp +++ b/include/boost/decimal/decimal64_t.hpp @@ -1693,7 +1693,11 @@ constexpr auto operator-(const decimal64_t lhs, const decimal64_t rhs) noexcept { return from_bits(detail::d64_nan_mask); } - + if (isinf(rhs)) + { + return -rhs; + } + return detail::check_non_finite(lhs, rhs); } #endif @@ -1746,6 +1750,11 @@ constexpr auto operator-(const Integer lhs, const decimal64_t rhs) noexcept #ifndef BOOST_DECIMAL_FAST_MATH if (not_finite(rhs)) { + if (isinf(rhs)) + { + return -rhs; + } + return detail::check_non_finite(rhs); } #endif diff --git a/include/boost/decimal/decimal_fast128_t.hpp b/include/boost/decimal/decimal_fast128_t.hpp index e61f9b285..8c89b969e 100644 --- a/include/boost/decimal/decimal_fast128_t.hpp +++ b/include/boost/decimal/decimal_fast128_t.hpp @@ -1076,6 +1076,10 @@ constexpr auto operator-(const decimal_fast128_t& lhs, const decimal_fast128_t& { return direct_init_d128(detail::d128_fast_qnan, 0, false); } + if (isinf(rhs)) + { + return -rhs; + } return detail::check_non_finite(lhs, rhs); } @@ -1113,6 +1117,11 @@ constexpr auto operator-(const Integer lhs, const decimal_fast128_t& rhs) noexce #ifndef BOOST_DECIMAL_FAST_MATH if (not_finite(rhs)) { + if (isinf(rhs)) + { + return -rhs; + } + return detail::check_non_finite(rhs); } #endif diff --git a/include/boost/decimal/decimal_fast32_t.hpp b/include/boost/decimal/decimal_fast32_t.hpp index 4380ba94f..1d737527c 100644 --- a/include/boost/decimal/decimal_fast32_t.hpp +++ b/include/boost/decimal/decimal_fast32_t.hpp @@ -1052,6 +1052,10 @@ constexpr auto operator-(const decimal_fast32_t lhs, decimal_fast32_t rhs) noexc { return direct_init(detail::d32_fast_qnan, UINT8_C((0))); } + if (isinf(rhs)) + { + return -rhs; + } return detail::check_non_finite(lhs, rhs); } @@ -1098,6 +1102,11 @@ constexpr auto operator-(const Integer lhs, const decimal_fast32_t rhs) noexcept #ifndef BOOST_DECIMAL_FAST_MATH if (!isfinite(rhs)) { + if (isinf(rhs)) + { + return -rhs; + } + return detail::check_non_finite(rhs); } #endif diff --git a/include/boost/decimal/decimal_fast64_t.hpp b/include/boost/decimal/decimal_fast64_t.hpp index 43d1ac240..35d7b8f0c 100644 --- a/include/boost/decimal/decimal_fast64_t.hpp +++ b/include/boost/decimal/decimal_fast64_t.hpp @@ -1177,6 +1177,10 @@ constexpr auto operator-(const decimal_fast64_t lhs, decimal_fast64_t rhs) noexc { return direct_init_d64(detail::d64_fast_qnan, 0, false); } + if (isinf(rhs)) + { + return -rhs; + } return detail::check_non_finite(lhs, rhs); } @@ -1223,6 +1227,11 @@ constexpr auto operator-(const Integer lhs, const decimal_fast64_t rhs) noexcept #ifndef BOOST_DECIMAL_FAST_MATH if (not_finite(rhs)) { + if (isinf(rhs)) + { + return -rhs; + } + return detail::check_non_finite(rhs); } #endif From da6ad83da9f05b19a120d7d1e87983bd3737416f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 19 Jan 2026 11:48:02 -0800 Subject: [PATCH 3/4] Add additional testing with NANs since inf should have no impact --- test/github_issue_1304.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/github_issue_1304.cpp b/test/github_issue_1304.cpp index dfbb01ad5..a6cb6c81d 100644 --- a/test/github_issue_1304.cpp +++ b/test/github_issue_1304.cpp @@ -21,6 +21,11 @@ void test() BOOST_TEST_EQ(-1000 - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(T{1000} - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(1000 - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); + + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() - std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::infinity() - std::numeric_limits::quiet_NaN())); + BOOST_TEST(isnan(std::numeric_limits::signaling_NaN() - std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::infinity() - std::numeric_limits::signaling_NaN())); } int main() From 87323592a9c5256cdd4cbac64c81b2df4caa4c7e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 20 Jan 2026 10:37:09 -0500 Subject: [PATCH 4/4] Fix nan operand case --- include/boost/decimal/decimal128_t.hpp | 2 +- include/boost/decimal/decimal32_t.hpp | 2 +- include/boost/decimal/decimal64_t.hpp | 2 +- include/boost/decimal/decimal_fast128_t.hpp | 2 +- include/boost/decimal/decimal_fast32_t.hpp | 2 +- include/boost/decimal/decimal_fast64_t.hpp | 2 +- test/github_issue_1304.cpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index b432d3b70..6e9693511 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1709,7 +1709,7 @@ constexpr auto operator-(const decimal128_t& lhs, const decimal128_t& rhs) noexc { return from_bits(detail::d128_nan_mask); } - if (isinf(rhs)) + if (isinf(rhs) && !isnan(lhs)) { return -rhs; } diff --git a/include/boost/decimal/decimal32_t.hpp b/include/boost/decimal/decimal32_t.hpp index 86c804ed1..0018665b6 100644 --- a/include/boost/decimal/decimal32_t.hpp +++ b/include/boost/decimal/decimal32_t.hpp @@ -1088,7 +1088,7 @@ constexpr auto operator-(const decimal32_t lhs, const decimal32_t rhs) noexcept { return from_bits(detail::d32_nan_mask); } - if (isinf(rhs)) + if (isinf(rhs) && !isnan(lhs)) { return -rhs; } diff --git a/include/boost/decimal/decimal64_t.hpp b/include/boost/decimal/decimal64_t.hpp index 7eebd1dea..a510705a2 100644 --- a/include/boost/decimal/decimal64_t.hpp +++ b/include/boost/decimal/decimal64_t.hpp @@ -1693,7 +1693,7 @@ constexpr auto operator-(const decimal64_t lhs, const decimal64_t rhs) noexcept { return from_bits(detail::d64_nan_mask); } - if (isinf(rhs)) + if (isinf(rhs) && !isnan(lhs)) { return -rhs; } diff --git a/include/boost/decimal/decimal_fast128_t.hpp b/include/boost/decimal/decimal_fast128_t.hpp index 8c89b969e..a7d247129 100644 --- a/include/boost/decimal/decimal_fast128_t.hpp +++ b/include/boost/decimal/decimal_fast128_t.hpp @@ -1076,7 +1076,7 @@ constexpr auto operator-(const decimal_fast128_t& lhs, const decimal_fast128_t& { return direct_init_d128(detail::d128_fast_qnan, 0, false); } - if (isinf(rhs)) + if (isinf(rhs) && !isnan(lhs)) { return -rhs; } diff --git a/include/boost/decimal/decimal_fast32_t.hpp b/include/boost/decimal/decimal_fast32_t.hpp index 1d737527c..9d431766c 100644 --- a/include/boost/decimal/decimal_fast32_t.hpp +++ b/include/boost/decimal/decimal_fast32_t.hpp @@ -1052,7 +1052,7 @@ constexpr auto operator-(const decimal_fast32_t lhs, decimal_fast32_t rhs) noexc { return direct_init(detail::d32_fast_qnan, UINT8_C((0))); } - if (isinf(rhs)) + if (isinf(rhs) && !isnan(lhs)) { return -rhs; } diff --git a/include/boost/decimal/decimal_fast64_t.hpp b/include/boost/decimal/decimal_fast64_t.hpp index 35d7b8f0c..4e258bd6d 100644 --- a/include/boost/decimal/decimal_fast64_t.hpp +++ b/include/boost/decimal/decimal_fast64_t.hpp @@ -1177,7 +1177,7 @@ constexpr auto operator-(const decimal_fast64_t lhs, decimal_fast64_t rhs) noexc { return direct_init_d64(detail::d64_fast_qnan, 0, false); } - if (isinf(rhs)) + if (isinf(rhs) && !isnan(lhs)) { return -rhs; } diff --git a/test/github_issue_1304.cpp b/test/github_issue_1304.cpp index a6cb6c81d..a66c307b9 100644 --- a/test/github_issue_1304.cpp +++ b/test/github_issue_1304.cpp @@ -21,7 +21,7 @@ void test() BOOST_TEST_EQ(-1000 - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(T{1000} - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(1000 - std::numeric_limits::infinity(), -std::numeric_limits::infinity()); - + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() - std::numeric_limits::infinity())); BOOST_TEST(isnan(std::numeric_limits::infinity() - std::numeric_limits::quiet_NaN())); BOOST_TEST(isnan(std::numeric_limits::signaling_NaN() - std::numeric_limits::infinity()));