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
42 changes: 42 additions & 0 deletions doc/modules/ROOT/pages/cmath.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -702,3 +702,45 @@ The function returns the decimal type with number of fractional digits equal to
`rescale` is similar to https://en.cppreference.com/w/cpp/numeric/math/trunc[trunc], and with the default precision argument of 0 it is identical.

NOTE: This function was previously known as `trunc_to` which was deprecated in v5.0.0 and removed in v6.0.0

=== `comparetotal`

[source, c++]
----
#include <boost/decimal/cmath.hpp>

namespace boost {
namespace decimal {

template <typename DecimalType>
constexpr bool comparetotal(DecimalType x, DecimalType y) noexcept;

} // namespace decimal
} // namespace boost
----

This function is an extended version of normal ordering to take into account payloads of NANs, and canonical forms.

Effects:

. If x < y returns `true`

. If x > y returns `false`

. If x == y:
.. -0 < +0 is `true`
.. +0 < -0 is `false`
.. If x and y have the same sign:
... If x and y are both negative, `comparetotal(x, y)` is `true` IFF the exponent of x pass:[>=] exponent of y
... Otherwise, `comparetotal(x, y)` is `true` IFF the exponent of x pass:[<=] y

. If x and y are unordered because x or y is a NAN:
.. `comparetotal(-NAN, y)` is `true`
.. `comparetotal(x, +NAN)` is `true`
.. If x and y are both NAN:
... `comparetotal(-NAN, +NAN)` returns `true`
... `comparetotal(+SNAN, +QNAN)` returns `true`
... `comparetotal(-QNAN, -SNAN)` returns `true`
... `comparetotal(NAN, NAN)`
.... If both `NAN` are positive returns `true` if the payload of `x` is less than the payload of `y`
.... If both `NAN` are negative returns `true` if the payload of `x` is greater than the payload of `y`
6 changes: 6 additions & 0 deletions include/boost/decimal/cmath.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
#include <boost/decimal/detail/cmath/rescale.hpp>
#include <boost/decimal/detail/cmath/beta.hpp>
#include <boost/decimal/detail/cmath/normalize.hpp>
#include <boost/decimal/detail/cmath/comparetotal.hpp>
#include <boost/decimal/numbers.hpp>

// Macros from 3.6.2
Expand Down Expand Up @@ -224,6 +225,11 @@ BOOST_DECIMAL_EXPORT constexpr auto quantexp(decimal64_t x) noexcept -> int
return quantexpd64(x);
}

BOOST_DECIMAL_EXPORT constexpr auto quantexp(decimal_fast64_t x) noexcept -> int
{
return quantexpd64f(x);
}

BOOST_DECIMAL_EXPORT constexpr auto quantexp(decimal128_t x) noexcept -> int
{
return quantexpd128(x);
Expand Down
15 changes: 15 additions & 0 deletions include/boost/decimal/decimal_fast64_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ BOOST_DECIMAL_EXPORT class decimal_fast64_t final
friend constexpr auto copysignd64f(decimal_fast64_t mag, decimal_fast64_t sgn) noexcept -> decimal_fast64_t;
friend constexpr auto scalbnd64f(decimal_fast64_t num, int exp) noexcept -> decimal_fast64_t;
friend constexpr auto scalblnd64f(decimal_fast64_t num, long exp) noexcept -> decimal_fast64_t;
friend constexpr auto quantexpd64f(decimal_fast64_t x) noexcept -> int;
};

#ifdef BOOST_DECIMAL_HAS_CONCEPTS
Expand Down Expand Up @@ -1471,6 +1472,20 @@ constexpr auto decimal_fast64_t::operator--(int) noexcept -> decimal_fast64_t&
return --(*this);
}

// Effects: if x is finite, returns its quantum exponent.
// Otherwise, a domain error occurs and INT_MIN is returned.
constexpr auto quantexpd64f(const decimal_fast64_t x) noexcept -> int
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (!isfinite(x))
{
return INT_MIN;
}
#endif

return static_cast<int>(x.unbiased_exponent());
}

constexpr auto scalblnd64f(decimal_fast64_t num, const long exp) noexcept -> decimal_fast64_t
{
#ifndef BOOST_DECIMAL_FAST_MATH
Expand Down
163 changes: 163 additions & 0 deletions include/boost/decimal/detail/cmath/comparetotal.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#ifndef BOOST_DECIMAL_DETAIL_CMATH_TOTAL_ORDER_HPP
#define BOOST_DECIMAL_DETAIL_CMATH_TOTAL_ORDER_HPP

#include <boost/decimal/fwd.hpp>
#include <boost/decimal/detail/config.hpp>
#include <boost/decimal/detail/concepts.hpp>
#include <boost/decimal/detail/promotion.hpp>
#include <boost/decimal/detail/cmath/nan.hpp>

namespace boost {
namespace decimal {

constexpr auto quantexp(decimal32_t x) noexcept -> int;
constexpr auto quantexp(decimal_fast32_t x) noexcept -> int;
constexpr auto quantexp(decimal64_t x) noexcept -> int;
constexpr auto quantexp(decimal_fast64_t x) noexcept -> int;
constexpr auto quantexp(decimal128_t x) noexcept -> int;
constexpr auto quantexp(decimal_fast128_t x) noexcept -> int;

namespace detail {

#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4127) // Conditional expression is constant
#endif

template <typename T>
constexpr auto total_ordering_impl(const T x, const T y) noexcept
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, T, bool)
{
// Part d: Check for unordered values
const auto x_nan {isnan(x)};
const auto x_neg {signbit(x)};
const auto y_nan {isnan(y)};
const auto y_neg {signbit(y)};

if (x_nan && x_neg && !y_nan)
{
// d.1
return true;
}
if (!x_nan && y_nan && !y_neg)
{
// d.2
return true;
}
if (x_nan && y_nan)
{
// d.3.i
if (x_neg && !y_neg)
{
return true;
}
// d.3.ii
const auto x_signaling {issignaling(x)};
const auto y_signaling {issignaling(y)};
if (x_signaling || y_signaling)
{
if (x_signaling && !y_signaling)
{
return !x_neg;
}
else if (!x_signaling && y_signaling)
{
return y_neg;
}
}
// d.3.iii
const auto x_payload {read_payload(x)};
const auto y_payload {read_payload(y)};

if (x_payload != y_payload)
{
if (!x_neg && !y_neg)
{
return x_payload < y_payload;
}
else if (x_neg && y_neg)
{
return x_payload > y_payload;
}
else if (x_neg && !y_neg)
{
return true;
}
else
{
return false;
}
}

return false;
}

if (x < y)
{
// part a
return true;
}
else if (x > y)
{
// part b
return false;
}
else
{
if (x == 0 && y == 0)
{
if (x_neg && !y_neg)
{
// c.1
return true;
}
else if (!x_neg && y_neg)
{
// c.2
return false;
}
}

BOOST_DECIMAL_IF_CONSTEXPR (detail::is_ieee_type_v<T>)
{
if (x_neg && y_neg)
{
// c.3.i
return quantexp(x) >= quantexp(y);
}
else
{
// c.3.ii
return quantexp(x) <= quantexp(y);
}
}
else
{
// Since things are normalized this will always be true
return true;
}
}
}

#ifdef _MSC_VER
# pragma warning(pop)
#endif

} // namespace detail

BOOST_DECIMAL_EXPORT template <typename T1, typename T2>
constexpr auto comparetotal(const T1 lhs, const T2 rhs) noexcept
BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, T1, detail::is_decimal_floating_point_v, T2, bool)
{
using larger_type = std::conditional_t<detail::decimal_val_v<T1> >= detail::decimal_val_v<T2>, T1, T2>;
return detail::total_ordering_impl(static_cast<larger_type>(lhs), static_cast<larger_type>(rhs));
}

} // namespace decimal
} // namespace boost

#endif // BOOST_DECIMAL_DETAIL_CMATH_TOTAL_ORDER_HPP
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ run test_tanh.cpp ;
run test_tgamma.cpp ;
run test_to_chars.cpp ;
run test_to_string.cpp ;
run test_total_ordering.cpp ;
run test_zeta.cpp ;

# Run the examples too
Expand Down
Loading
Loading