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
2 changes: 1 addition & 1 deletion .github/workflows/amalgamate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
return 2;
}
if (add_sat(a, b) != (std::numeric_limits<uint128_t>::max)())
if (saturating_add(a, b) != (std::numeric_limits<uint128_t>::max)())
{
return 3;
}
Expand Down
10 changes: 5 additions & 5 deletions doc/modules/ROOT/pages/api_reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -249,19 +249,19 @@ Listed by analogous STL header.
|===
| Function | Description

| xref:numeric.adoc#sat_arith[`add_sat`]
| xref:numeric.adoc#sat_arith[`saturating_add`]
| Saturating addition

| xref:numeric.adoc#sat_arith[`sub_sat`]
| xref:numeric.adoc#sat_arith[`saturating_sub`]
| Saturating subtraction

| xref:numeric.adoc#sat_arith[`mul_sat`]
| xref:numeric.adoc#sat_arith[`saturating_mul`]
| Saturating multiplication

| xref:numeric.adoc#sat_arith[`div_sat`]
| xref:numeric.adoc#sat_arith[`saturating_div`]
| Saturating division

| xref:numeric.adoc#saturating_cast[`saturate_cast`]
| xref:numeric.adoc#saturating_cast[`saturating_cast`]
| Saturating type cast

| xref:numeric.adoc#gcd[`gcd`]
Expand Down
16 changes: 8 additions & 8 deletions doc/modules/ROOT/pages/examples.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -219,20 +219,20 @@ int128_t max = 170141183460469231731687303715884105727
int128_t min = -170141183460469231731687303715884105728

=== Saturating Addition and Subtraction ===
add_sat(uint_max, uint_max) = 340282366920938463463374607431768211455 (saturates to uint_max)
sub_sat(0, uint_max) = 0 (saturates to 0, not underflow)
saturating_add(uint_max, uint_max) = 340282366920938463463374607431768211455 (saturates to uint_max)
saturating_sub(0, uint_max) = 0 (saturates to 0, not underflow)

=== Saturating Multiplication ===
mul_sat(int_max, 2) = 170141183460469231731687303715884105727 (saturates to int_max)
mul_sat(-(int_max - 2), 5) = -170141183460469231731687303715884105728 (saturates to int_min)
saturating_mul(int_max, 2) = 170141183460469231731687303715884105727 (saturates to int_max)
saturating_mul(-(int_max - 2), 5) = -170141183460469231731687303715884105728 (saturates to int_min)

=== Saturating Division ===
div_sat(int_min, -1) = 170141183460469231731687303715884105727 (saturates to int_max; normally this overflows)
saturating_div(int_min, -1) = 170141183460469231731687303715884105727 (saturates to int_max; normally this overflows)

=== Saturating Casts ===
saturate_cast<int128_t>(uint_max) = 170141183460469231731687303715884105727 (saturates to int_max)
saturate_cast<int64_t>(int_max) = 9223372036854775807 (saturates to INT64_MAX)
saturate_cast<int32_t>(uint_max) = 2147483647 (saturates to INT32_MAX)
saturating_cast<int128_t>(uint_max) = 170141183460469231731687303715884105727 (saturates to int_max)
saturating_cast<int64_t>(int_max) = 9223372036854775807 (saturates to INT64_MAX)
saturating_cast<int32_t>(uint_max) = 2147483647 (saturates to INT32_MAX)
----
====

Expand Down
24 changes: 12 additions & 12 deletions doc/modules/ROOT/pages/numeric.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,29 @@ The following functions are provided for saturating arithmetic, and they *do not
namespace boost {
namespace int128 {

BOOST_INT128_HOST_DEVICE constexpr uint128_t add_sat(uint128_t lhs, uint128_t rhs) noexcept;
BOOST_INT128_HOST_DEVICE constexpr uint128_t saturating_add(uint128_t lhs, uint128_t rhs) noexcept;

BOOST_INT128_HOST_DEVICE constexpr int128_t add_sat(int128_t lhs, int128_t rhs) noexcept;
BOOST_INT128_HOST_DEVICE constexpr int128_t saturating_add(int128_t lhs, int128_t rhs) noexcept;

BOOST_INT128_HOST_DEVICE constexpr uint128_t sub_sat(uint128_t lhs, uint128_t rhs) noexcept;
BOOST_INT128_HOST_DEVICE constexpr uint128_t saturating_sub(uint128_t lhs, uint128_t rhs) noexcept;

BOOST_INT128_HOST_DEVICE constexpr int128_t sub_sat(int128_t lhs, int128_t rhs) noexcept;
BOOST_INT128_HOST_DEVICE constexpr int128_t saturating_sub(int128_t lhs, int128_t rhs) noexcept;

BOOST_INT128_HOST_DEVICE constexpr uint128_t mul_sat(uint128_t lhs, uint128_t rhs) noexcept;
BOOST_INT128_HOST_DEVICE constexpr uint128_t saturating_mul(uint128_t lhs, uint128_t rhs) noexcept;

BOOST_INT128_HOST_DEVICE constexpr int128_t mul_sat(int128_t lhs, int128_t rhs) noexcept;
BOOST_INT128_HOST_DEVICE constexpr int128_t saturating_mul(int128_t lhs, int128_t rhs) noexcept;

BOOST_INT128_HOST_DEVICE constexpr uint128_t div_sat(uint128_t lhs, uint128_t rhs) noexcept;
BOOST_INT128_HOST_DEVICE constexpr uint128_t saturating_div(uint128_t lhs, uint128_t rhs) noexcept;

BOOST_INT128_HOST_DEVICE constexpr int128_t div_sat(int128_t lhs, int128_t rhs) noexcept;
BOOST_INT128_HOST_DEVICE constexpr int128_t saturating_div(int128_t lhs, int128_t rhs) noexcept;

} // namespace int128
} // namespace boost

----

For `div_sat` the only value that can overflow is the signed `INT128_MIN / -1`, which saturates to `INT128_MAX`; every other non-zero divisor gives the same result as the plain `operator/`.
Division by zero is a precondition violation and therefore undefined behavior (the divisor must be non-zero), exactly as for the built-in `__int128` types and the plain division operator; `div_sat` adds no zero-divisor check.
For `saturating_div` the only value that can overflow is the signed `INT128_MIN / -1`, which saturates to `INT128_MAX`; every other non-zero divisor gives the same result as the plain `operator/`.
Division by zero is a precondition violation and therefore undefined behavior (the divisor must be non-zero), exactly as for the built-in `__int128` types and the plain division operator; `saturating_div` adds no zero-divisor check.

[#saturating_cast]
== Saturating Cast
Expand All @@ -62,10 +62,10 @@ namespace boost {
namespace int128 {

template <typename TargetType>
BOOST_INT128_HOST_DEVICE constexpr TargetType saturate_cast(uint128_t value) noexcept;
BOOST_INT128_HOST_DEVICE constexpr TargetType saturating_cast(uint128_t value) noexcept;

template <typename TargetType>
BOOST_INT128_HOST_DEVICE constexpr TargetType saturate_cast(int128_t value) noexcept;
BOOST_INT128_HOST_DEVICE constexpr TargetType saturating_cast(int128_t value) noexcept;

} // namespace int128
} // namespace boost
Expand Down
20 changes: 10 additions & 10 deletions examples/saturating_arithmetic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,34 @@ int main()

// Saturating arithmetic returns max on overflow, or min on underflow rather than rolling over
std::cout << "\n=== Saturating Addition and Subtraction ===" << std::endl;
std::cout << "add_sat(uint_max, uint_max) = " << boost::int128::add_sat(uint_max, uint_max)
std::cout << "saturating_add(uint_max, uint_max) = " << boost::int128::saturating_add(uint_max, uint_max)
<< " (saturates to uint_max)" << std::endl;
std::cout << "sub_sat(0, uint_max) = " << boost::int128::sub_sat(uint128_t{0}, uint_max)
std::cout << "saturating_sub(0, uint_max) = " << boost::int128::saturating_sub(uint128_t{0}, uint_max)
<< " (saturates to 0, not underflow)" << std::endl;

// This is especially useful for signed types since rollover is undefined
std::cout << "\n=== Saturating Multiplication ===" << std::endl;
std::cout << "mul_sat(int_max, 2) = " << boost::int128::mul_sat(int_max, 2)
std::cout << "saturating_mul(int_max, 2) = " << boost::int128::saturating_mul(int_max, 2)
<< " (saturates to int_max)" << std::endl;
std::cout << "mul_sat(-(int_max - 2), 5) = " << boost::int128::mul_sat(-(int_max - 2), 5)
std::cout << "saturating_mul(-(int_max - 2), 5) = " << boost::int128::saturating_mul(-(int_max - 2), 5)
<< " (saturates to int_min)" << std::endl;

// The only case in the library where div_sat overflows is x = int_min and y = -1
// The only case in the library where saturating_div overflows is x = int_min and y = -1
std::cout << "\n=== Saturating Division ===" << std::endl;
std::cout << "div_sat(int_min, -1) = " << boost::int128::div_sat(int_min, -1)
std::cout << "saturating_div(int_min, -1) = " << boost::int128::saturating_div(int_min, -1)
<< " (saturates to int_max; normally this overflows)" << std::endl;

// saturate_cast allows types to be safely converted without rollover behavior
// saturating_cast allows types to be safely converted without rollover behavior
std::cout << "\n=== Saturating Casts ===" << std::endl;
std::cout << "saturate_cast<int128_t>(uint_max) = " << boost::int128::saturate_cast<int128_t>(uint_max)
std::cout << "saturating_cast<int128_t>(uint_max) = " << boost::int128::saturating_cast<int128_t>(uint_max)
<< " (saturates to int_max)" << std::endl;

// You can also cast to builtin types
std::cout << "saturate_cast<int64_t>(int_max) = " << boost::int128::saturate_cast<std::int64_t>(int_max)
std::cout << "saturating_cast<int64_t>(int_max) = " << boost::int128::saturating_cast<std::int64_t>(int_max)
<< " (saturates to INT64_MAX)" << std::endl;

// Even of different signedness as this is treated like a static cast
std::cout << "saturate_cast<int32_t>(uint_max) = " << boost::int128::saturate_cast<std::int32_t>(uint_max)
std::cout << "saturating_cast<int32_t>(uint_max) = " << boost::int128::saturating_cast<std::int32_t>(uint_max)
<< " (saturates to INT32_MAX)" << std::endl;

return 0;
Expand Down
20 changes: 10 additions & 10 deletions include/boost/int128/numeric.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ BOOST_INT128_INLINE_CONSTEXPR bool is_reduced_integer_v {reduced_integers<Intege

} // namespace detail

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t add_sat(const uint128_t x, const uint128_t y) noexcept
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t saturating_add(const uint128_t x, const uint128_t y) noexcept
{
const auto z {x + y};

Expand All @@ -65,7 +65,7 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t add_sat(const u
return z;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t sub_sat(const uint128_t x, const uint128_t y) noexcept
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t saturating_sub(const uint128_t x, const uint128_t y) noexcept
{
const auto z {x - y};

Expand All @@ -83,7 +83,7 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t sub_sat(const u
# pragma warning(disable : 4146) // Unary minus applied to unsigned type
#endif

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t add_sat(const int128_t x, const int128_t y) noexcept
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t saturating_add(const int128_t x, const int128_t y) noexcept
{
// Detect overflow BEFORE the addition to avoid signed overflow UB.
// When both are non-negative: overflow iff x > max - y (subtraction safe: max - non_negative >= 0)
Expand All @@ -108,7 +108,7 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t add_sat(const in
return x + y;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t sub_sat(const int128_t x, const int128_t y) noexcept
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t saturating_sub(const int128_t x, const int128_t y) noexcept
{
// Detect overflow BEFORE the subtraction to avoid signed overflow UB.
// Positive overflow: x >= 0 and y < 0 and x > max + y (safe: max + negative < max)
Expand Down Expand Up @@ -137,7 +137,7 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t sub_sat(const in
# pragma warning(pop)
#endif

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t mul_sat(const uint128_t x, const uint128_t y) noexcept
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t saturating_mul(const uint128_t x, const uint128_t y) noexcept
{
const auto x_bits {bit_width(x)};
const auto y_bits {bit_width(y)};
Expand All @@ -150,7 +150,7 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t mul_sat(const u
return x * y;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t mul_sat(const int128_t x, const int128_t y) noexcept
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t saturating_mul(const int128_t x, const int128_t y) noexcept
{
const auto x_bits {bit_width(static_cast<uint128_t>(abs(x)))};
const auto y_bits {bit_width(static_cast<uint128_t>(abs(y)))};
Expand All @@ -171,12 +171,12 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t mul_sat(const in
return res;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t div_sat(const uint128_t x, const uint128_t y) noexcept
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t saturating_div(const uint128_t x, const uint128_t y) noexcept
{
return x / y;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t div_sat(const int128_t x, const int128_t y) noexcept
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t saturating_div(const int128_t x, const int128_t y) noexcept
{
if (BOOST_INT128_UNLIKELY(x == (std::numeric_limits<int128_t>::min)() && y == -1))
{
Expand All @@ -193,7 +193,7 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t div_sat(const in
#endif

BOOST_INT128_EXPORT template <typename TargetType, std::enable_if_t<detail::is_reduced_integer_v<TargetType>, bool> = true>
BOOST_INT128_HOST_DEVICE constexpr TargetType saturate_cast(const uint128_t value) noexcept
BOOST_INT128_HOST_DEVICE constexpr TargetType saturating_cast(const uint128_t value) noexcept
{
BOOST_INT128_IF_CONSTEXPR (std::is_same<uint128_t, TargetType>::value)
{
Expand All @@ -215,7 +215,7 @@ BOOST_INT128_HOST_DEVICE constexpr TargetType saturate_cast(const uint128_t valu
#endif

BOOST_INT128_EXPORT template <typename TargetType, std::enable_if_t<detail::is_reduced_integer_v<TargetType>, bool> = true>
BOOST_INT128_HOST_DEVICE constexpr TargetType saturate_cast(const int128_t value) noexcept
BOOST_INT128_HOST_DEVICE constexpr TargetType saturating_cast(const int128_t value) noexcept
{
BOOST_INT128_IF_CONSTEXPR (std::is_same<int128_t, TargetType>::value)
{
Expand Down
16 changes: 8 additions & 8 deletions test/cuda_jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ run test_signed_cstdlib_div.cu ;
run test_signed_to_unsigned_conversion.cu ;
run test_unsigned_to_signed_conversion.cu ;

run test_unsigned_add_sat.cu ;
run test_signed_add_sat.cu ;
run test_unsigned_sub_sat.cu ;
run test_signed_sub_sat.cu ;
run test_unsigned_mul_sat.cu ;
run test_signed_mul_sat.cu ;
run test_unsigned_div_sat.cu ;
run test_signed_div_sat.cu ;
run test_unsigned_saturating_add.cu ;
run test_signed_saturating_add.cu ;
run test_unsigned_saturating_sub.cu ;
run test_signed_saturating_sub.cu ;
run test_unsigned_saturating_mul.cu ;
run test_signed_saturating_mul.cu ;
run test_unsigned_saturating_div.cu ;
run test_signed_saturating_div.cu ;
run test_unsigned_gcd.cu ;
run test_signed_gcd.cu ;
run test_unsigned_lcm.cu ;
Expand Down
16 changes: 8 additions & 8 deletions test/sycl_jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ run test_signed_cstdlib_div_sycl.cpp ;
run test_signed_to_unsigned_conversion_sycl.cpp ;
run test_unsigned_to_signed_conversion_sycl.cpp ;

run test_unsigned_add_sat_sycl.cpp ;
run test_signed_add_sat_sycl.cpp ;
run test_unsigned_sub_sat_sycl.cpp ;
run test_signed_sub_sat_sycl.cpp ;
run test_unsigned_mul_sat_sycl.cpp ;
run test_signed_mul_sat_sycl.cpp ;
run test_unsigned_div_sat_sycl.cpp ;
run test_signed_div_sat_sycl.cpp ;
run test_unsigned_saturating_add_sycl.cpp ;
run test_signed_saturating_add_sycl.cpp ;
run test_unsigned_saturating_sub_sycl.cpp ;
run test_signed_saturating_sub_sycl.cpp ;
run test_unsigned_saturating_mul_sycl.cpp ;
run test_signed_saturating_mul_sycl.cpp ;
run test_unsigned_saturating_div_sycl.cpp ;
run test_signed_saturating_div_sycl.cpp ;
run test_unsigned_gcd_sycl.cpp ;
run test_signed_gcd_sycl.cpp ;
run test_unsigned_lcm_sycl.cpp ;
Expand Down
Loading
Loading