Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions include/boost/decimal/detail/cmath/fmax.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,29 @@ constexpr auto fmax(const T1 lhs, const T2 rhs) noexcept
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs) && !isnan(rhs))
{
return static_cast<promoted_type>(rhs);
if (issignaling(lhs))
{
return nan_conversion(lhs);
}
else
{
return static_cast<promoted_type>(rhs);
}
}
else if ((!isnan(lhs) && isnan(rhs)) ||
(isnan(lhs) && isnan(rhs)))
else if ((!isnan(lhs) && isnan(rhs)) || (isnan(lhs) && isnan(rhs)))
{
return static_cast<promoted_type>(lhs);
if (issignaling(lhs))
{
return nan_conversion(lhs);
}
else if (issignaling(rhs))
{
return nan_conversion(rhs);
}
else
{
return static_cast<promoted_type>(lhs);
}
}
#endif

Expand Down
25 changes: 21 additions & 4 deletions include/boost/decimal/detail/cmath/fmin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,29 @@ constexpr auto fmin(const T1 lhs, const T2 rhs) noexcept
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs) && !isnan(rhs))
{
return static_cast<promoted_type>(rhs);
if (issignaling(lhs))
{
return nan_conversion(lhs);
}
else
{
return static_cast<promoted_type>(rhs);
}
}
else if ((!isnan(lhs) && isnan(rhs)) ||
(isnan(lhs) && isnan(rhs)))
else if ((!isnan(lhs) && isnan(rhs)) || (isnan(lhs) && isnan(rhs)))
{
return static_cast<promoted_type>(lhs);
if (issignaling(lhs))
{
return nan_conversion(lhs);
}
else if (issignaling(rhs))
{
return nan_conversion(rhs);
}
else
{
return static_cast<promoted_type>(lhs);
}
}
#endif

Expand Down
6 changes: 4 additions & 2 deletions include/boost/decimal/detail/cmath/nan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,19 @@ BOOST_DECIMAL_EXPORT template <typename T>
constexpr auto read_payload(const T value) noexcept
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_ieee_type_v, T, typename T::significand_type)
{
const auto positive_value {signbit(value) ? -value : value};

if (!isnan(value))
{
return 0U;
}
else if (issignaling(value))
{
return value.bits_ ^ std::numeric_limits<T>::signaling_NaN().bits_;
return positive_value.bits_ ^ std::numeric_limits<T>::signaling_NaN().bits_;
}
else
{
return value.bits_ ^ std::numeric_limits<T>::quiet_NaN().bits_;
return positive_value.bits_ ^ std::numeric_limits<T>::quiet_NaN().bits_;
}
}

Expand Down
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ run github_issue_1112.cpp ;
run github_issue_1174.cpp ;
run github_issue_1260.cpp ;
run github_issue_1294.cpp ;
run github_issue_1299.cpp ;

run link_1.cpp link_2.cpp link_3.cpp ;
run quick.cpp ;
Expand Down
145 changes: 145 additions & 0 deletions test/github_issue_1299.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2025 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/1299

#include <boost/decimal.hpp>
#include <boost/core/lightweight_test.hpp>
#include <limits>

using namespace boost::decimal;

enum class result_t
{
lhs,
rhs,
qnan
};

enum class result_sign_t
{
positive,
negative
};

template <typename T>
void test_fmax_value(const T lhs, const T rhs, const result_t result, const result_sign_t sign = result_sign_t::positive, const int payload_value = 0)
{
const auto res {fmax(lhs, rhs)};

switch (result)
{
case result_t::lhs:
BOOST_TEST_EQ(lhs, res);
break;

case result_t::rhs:
BOOST_TEST_EQ(rhs, res);
break;

case result_t::qnan:
BOOST_TEST(isnan(res));
BOOST_TEST(!issignaling(res));

if (sign == result_sign_t::negative)
{
BOOST_TEST(signbit(res));
}

if (payload_value)
{
const auto payload {read_payload(res)};
BOOST_TEST_EQ(static_cast<decltype(payload)>(payload_value), payload);
}

break;
}
}

template <typename T>
void test_fmin_value(const T lhs, const T rhs, const result_t result, const result_sign_t sign = result_sign_t::positive, const int payload_value = 0)
{
const auto res {fmin(lhs, rhs)};

switch (result)
{
case result_t::lhs:
BOOST_TEST_EQ(lhs, res);
break;

case result_t::rhs:
BOOST_TEST_EQ(rhs, res);
break;

case result_t::qnan:
BOOST_TEST(isnan(res));
BOOST_TEST(!issignaling(res));

if (sign == result_sign_t::negative)
{
BOOST_TEST(signbit(res));
}

if (payload_value)
{
const auto payload {read_payload(res)};
BOOST_TEST_EQ(static_cast<decltype(payload)>(payload_value), payload);
}

break;
}
}

template <typename T>
void test_driver()
{
test_fmax_value(T{5}, T{42}, result_t::rhs);
test_fmax_value(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::quiet_NaN(), result_t::lhs);
test_fmax_value(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::quiet_NaN(), result_t::lhs);
test_fmax_value(T{5}, std::numeric_limits<T>::quiet_NaN(), result_t::lhs);

// Any operation on SNAN is invalid and returns QNAN with the same payload (as applicable)
test_fmax_value(std::numeric_limits<T>::signaling_NaN(), T{5}, result_t::qnan);
test_fmax_value(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::signaling_NaN(), result_t::qnan);
test_fmax_value(std::numeric_limits<T>::signaling_NaN(), std::numeric_limits<T>::quiet_NaN(), result_t::qnan);

T snan_payload {"-sNaN97"};
test_fmax_value(snan_payload, std::numeric_limits<T>::quiet_NaN(), result_t::qnan, result_sign_t::negative, 97);
test_fmax_value(std::numeric_limits<T>::quiet_NaN(), snan_payload, result_t::qnan, result_sign_t::negative, 97);

snan_payload = -snan_payload;
T qnan_payload {"NaN100"};
test_fmax_value(snan_payload, qnan_payload, result_t::qnan, result_sign_t::positive, 97);

test_fmin_value(T{5}, T{42}, result_t::lhs);
test_fmin_value(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::quiet_NaN(), result_t::lhs);
test_fmin_value(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::quiet_NaN(), result_t::lhs);
test_fmin_value(T{5}, std::numeric_limits<T>::quiet_NaN(), result_t::lhs);

// Any operation on SNAN is invalid and returns QNAN with the same payload (as applicable)
test_fmin_value(std::numeric_limits<T>::signaling_NaN(), T{5}, result_t::qnan);
test_fmin_value(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::signaling_NaN(), result_t::qnan);
test_fmin_value(std::numeric_limits<T>::signaling_NaN(), std::numeric_limits<T>::quiet_NaN(), result_t::qnan);

snan_payload = T{"-sNaN97"};
test_fmin_value(snan_payload, std::numeric_limits<T>::quiet_NaN(), result_t::qnan, result_sign_t::negative, 97);
test_fmin_value(std::numeric_limits<T>::quiet_NaN(), snan_payload, result_t::qnan, result_sign_t::negative, 97);

snan_payload = -snan_payload;
qnan_payload = T{"NaN100"};
test_fmin_value(snan_payload, qnan_payload, result_t::qnan, result_sign_t::positive, 97);
}

int main()
{
test_driver<decimal32_t>();
test_driver<decimal64_t>();
test_driver<decimal128_t>();

test_driver<decimal_fast32_t>();
test_driver<decimal_fast64_t>();
test_driver<decimal_fast128_t>();

return boost::report_errors();
}