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
4 changes: 3 additions & 1 deletion doc/modules/ROOT/pages/cstdlib.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ BOOST_INT128_HOST_DEVICE constexpr i128div_t div(int128_t lhs, int128_t rhs) noe
} // namespace boost
----

The result of `quot` and `rem` from this function, including the sign, are the same as if you performed division and modulo separately.
For any non-zero divisor the `quot` and `rem` values, including their sign, are the same as if you performed division and modulo separately.

Division by zero is handled differently from the `operator/` and `operator%` operators. Those operators leave a zero divisor undefined (matching the built-in `__int128` types), whereas `div` detects a zero divisor and returns a zero-initialized result (`quot == 0` and `rem == 0`) instead. A zero numerator with a non-zero divisor likewise returns `{0, 0}`.
8 changes: 8 additions & 0 deletions doc/modules/ROOT/pages/int128_t.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ Specifically:
* For shift operators, the result type follows the LHS: `int128_t << T` and `int128_t >> T` always return `int128_t`, regardless of `T`.
* All comparison operators return `bool`, with the comparison performed on the operands after conversion to the common type.

NOTE: Division and remainder match `__int128` for every input except the two the built-in leaves undefined. Division or remainder by zero is likewise undefined behavior: the library adds no zero-divisor check, so it behaves like the built-in (a hardware trap on platforms that fault on integer division by zero) and is a hard compile-time error in a constant expression. The signed overflow `INT128_MIN / -1`, whose true quotient is not representable, is the one case the library defines rather than leaving undefined: it wraps to `INT128_MIN` (and `INT128_MIN % -1` is `0`), matching the two's-complement wrap the library applies to other overflowing signed operations such as `INT128_MIN * -1`. See xref:int128_t.adoc#i128_math_operators[Division] and Modulo below.

See xref:mixed_type_ops.adoc[Mixed Type Operations] for the full set of cross-type signatures and detailed result-type rules.

[#i128_constructors]
Expand Down Expand Up @@ -490,6 +492,9 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t lhs, const
----

Returns as an `int128_t` the quotient of `lhs` and `rhs` without exception.
The quotient truncates toward zero, and for any `rhs` other than `0` (and other than the overflow noted below) the result is identical to the built-in `__int128` division.
Division by zero is undefined behavior, exactly as for `__int128`: the library adds no zero-divisor check (so the generated code matches the built-in and traps where the hardware faults on integer division by zero), and a zero divisor seen during constant evaluation is a hard compile-time error.
The signed overflow `INT128_MIN / -1` is defined to wrap to `INT128_MIN` rather than being left undefined as it is for the built-in.
This operation is only defined for integers and is subject to mixed sign limitations discussed xref:int128_t.adoc#i128_operator_behavior[above].

=== Modulo
Expand All @@ -506,6 +511,9 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t lhs, const
----

Returns as an `int128_t` the remainder of `lhs` and `rhs` without exception.
The remainder takes the sign of the dividend `lhs`, and for any `rhs` other than `0` the result is identical to the built-in `__int128` remainder, so that `(lhs / rhs) * rhs + lhs % rhs == lhs`.
Remainder by zero is undefined behavior, exactly as for `__int128`: the library adds no zero-divisor check (so the generated code matches the built-in and traps where the hardware faults on integer division by zero), and a zero divisor seen during constant evaluation is a hard compile-time error.
The signed overflow `INT128_MIN % -1` is defined to be `0` rather than being left undefined as it is for the built-in.
This operation is only defined for integers and is subject to mixed sign limitations discussed xref:int128_t.adoc#i128_operator_behavior[above].

[#i128_abs]
Expand Down
3 changes: 3 additions & 0 deletions doc/modules/ROOT/pages/numeric.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t div_sat(int128_t lhs, int128_t rhs)

----

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.

[#saturating_cast]
== Saturating Cast

Expand Down
6 changes: 6 additions & 0 deletions doc/modules/ROOT/pages/uint128_t.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ Specifically:

For example, `uint128_t{5} > -1` returns `false` because the signed `-1` converts to a value greater than `5` under unsigned 128-bit arithmetic, exactly as `(unsigned __int128){5} > -1` would.

NOTE: The results match `unsigned __int128` for every input with a non-zero divisor. Division or remainder by zero is the one exception, and it is handled exactly as the built-in type handles it: as undefined behavior. The library performs no zero-divisor check, so a `uint128_t` divided or taken modulo by zero behaves like the built-in operation (a hardware trap on platforms that fault on integer division by zero), and in a constant expression it is a hard compile-time error just as `1u / 0u` is. See xref:uint128_t.adoc#u128_math_operators[Division] and Modulo below.

See xref:mixed_type_ops.adoc[Mixed Type Operations] for the full set of cross-type signatures and detailed result-type rules.

[#u128_constructors]
Expand Down Expand Up @@ -486,6 +488,8 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator/(const uint128_t lhs, cons
----

Returns as a `uint128_t` the quotient of `lhs` and `rhs` without exception.
For any non-zero `rhs` the result is identical to the built-in `unsigned __int128` division.
Division by zero is undefined behavior, exactly as for `unsigned __int128`: the library adds no zero-divisor check (so the generated code matches the built-in and traps where the hardware faults on integer division by zero), and a zero divisor seen during constant evaluation is a hard compile-time error.
This operation is only defined for integers and is subject to mixed sign limitations discussed xref:uint128_t.adoc#u128_operator_behavior[above].

=== Modulo
Expand All @@ -502,6 +506,8 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator%(const uint128_t lhs, cons
----

Returns as a `uint128_t` the remainder of `lhs` and `rhs` without exception.
For any non-zero `rhs` the result is identical to the built-in `unsigned __int128` remainder.
Remainder by zero is undefined behavior, exactly as for `unsigned __int128`: the library adds no zero-divisor check (so the generated code matches the built-in and traps where the hardware faults on integer division by zero), and a zero divisor seen during constant evaluation is a hard compile-time error.
This operation is only defined for integers and is subject to mixed sign limitations discussed xref:uint128_t.adoc#u128_operator_behavior[above].

[#u128_limits]
Expand Down
24 changes: 16 additions & 8 deletions include/boost/int128/detail/int128_imp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2207,7 +2207,8 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator/(const
{
if (BOOST_INT128_UNLIKELY(rhs == 0))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

constexpr int128_t min_val {INT64_MIN, 0};
Expand Down Expand Up @@ -2259,7 +2260,8 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t lhs, const

if (BOOST_INT128_UNLIKELY(rhs == 0))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

const auto abs_lhs {abs(lhs)};
Expand All @@ -2274,7 +2276,8 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator/(const UnsignedInteger lhs,
{
if (BOOST_INT128_UNLIKELY(rhs == 0))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

if (rhs.high != 0 && rhs.high != -1)
Expand Down Expand Up @@ -2307,7 +2310,8 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t lhs, const

if (BOOST_INT128_UNLIKELY(rhs == 0))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

int128_t quotient {};
Expand All @@ -2333,7 +2337,8 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator/(const SignedInteger lhs, c
{
if (BOOST_INT128_UNLIKELY(rhs == 0))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

if (rhs.high != 0 && rhs.high != -1)
Expand Down Expand Up @@ -2444,7 +2449,8 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t lhs, const

if (BOOST_INT128_UNLIKELY(rhs == 0))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

int128_t quotient {};
Expand All @@ -2464,7 +2470,8 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator%(const UnsignedInteger lhs,

if (BOOST_INT128_UNLIKELY(rhs == 0))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

const auto abs_rhs {abs(rhs)};
Expand Down Expand Up @@ -2495,7 +2502,8 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t lhs, const
{
if (rhs == 0)
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

constexpr int128_t min_val {INT64_MIN, 0};
Expand Down
18 changes: 12 additions & 6 deletions include/boost/int128/detail/uint128_imp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2449,7 +2449,8 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator/(const uint128_t lhs, cons

if (BOOST_INT128_UNLIKELY(rhs == 0U))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

if (lhs < rhs)
Expand All @@ -2471,7 +2472,8 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator/(const UnsignedInteger lhs

if (BOOST_INT128_UNLIKELY(rhs == 0U))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

if (lhs < rhs)
Expand All @@ -2486,7 +2488,8 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator/(const uint128_t lhs, cons
{
if (BOOST_INT128_UNLIKELY(rhs == 0U))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

if (lhs < rhs)
Expand Down Expand Up @@ -2610,7 +2613,8 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator%(const uint128_t lhs, cons

if (BOOST_INT128_UNLIKELY(rhs == 0U))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}

if (lhs.high != 0)
Expand All @@ -2635,7 +2639,8 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator%(const UnsignedInteger lhs

if (BOOST_INT128_UNLIKELY(rhs == 0U))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}
else if (rhs > lhs)
{
Expand All @@ -2649,7 +2654,8 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator%(const uint128_t lhs, cons
{
if (BOOST_INT128_UNLIKELY(rhs == 0U))
{
return {0, 0};
// Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable.
BOOST_INT128_UNREACHABLE;
}
if (rhs > lhs)
{
Expand Down
11 changes: 2 additions & 9 deletions test/test_i128.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,10 +944,7 @@ void test_operator_div()
const boost::int128::int128_t check_2_value {value2};
BOOST_TEST(check_1_value == (emulated_value / check_2_value));

// Shouldn't crash
BOOST_TEST(check_2_value / IntType(0) == 0);
BOOST_TEST(check_2_value / static_cast<boost::int128::int128_t>(0) == 0);
BOOST_TEST(value / static_cast<boost::int128::int128_t>(0) == 0);
// Division by zero is undefined behavior (matching the builtin __int128 types), so it is not tested.

// Always 0
BOOST_TEST(small_emulated_value / emulated_value == 0);
Expand Down Expand Up @@ -1063,11 +1060,7 @@ void test_operator_mod()
const boost::int128::int128_t check_2_value {value2};
BOOST_TEST(check_1_value == (emulated_value % check_2_value));

// Shouldn't crash
// Codecov has these marked as partials which is as long as they pass is irrelevant
BOOST_TEST(check_2_value % IntType(0) == 0); // LCOV_EXCL_LINE
BOOST_TEST(value % static_cast<boost::int128::int128_t>(0) == 0); // LCOV_EXCL_LINE
BOOST_TEST(small_emulated_value % static_cast<boost::int128::int128_t>(0) == 0); // LCOV_EXCL_LINE
// Remainder by zero is undefined behavior (matching the builtin __int128 types), so it is not tested.
BOOST_TEST(small_emulated_value % emulated_value == small_emulated_value); // LCOV_EXCL_LINE
BOOST_TEST(small_emulated_value % small_emulated_value == 0); // LCOV_EXCL_LINE
}
Expand Down
9 changes: 2 additions & 7 deletions test/test_u128.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1133,9 +1133,7 @@ void test_operator_div()
const boost::int128::uint128_t check_2_value {value2};
BOOST_TEST(check_1_value == (emulated_value / check_2_value));

// Shouldn't crash
BOOST_TEST(check_2_value / IntType(0) == 0);
BOOST_TEST(value / static_cast<boost::int128::uint128_t>(0) == 0);
// Division by zero is undefined behavior (matching the builtin __int128 types), so it is not tested.

// Always 0
BOOST_TEST(small_emulated_value / emulated_value == 0);
Expand Down Expand Up @@ -1235,10 +1233,7 @@ void test_operator_mod()
const boost::int128::uint128_t check_2_value {value2};
BOOST_TEST(check_1_value == (emulated_value % check_2_value));

// Shouldn't crash
BOOST_TEST(check_2_value % IntType(0) == 0);
BOOST_TEST(value % static_cast<boost::int128::uint128_t>(0) == 0);
BOOST_TEST(small_emulated_value % static_cast<boost::int128::uint128_t>(0) == 0);
// Remainder by zero is undefined behavior (matching the builtin __int128 types), so it is not tested.

BOOST_TEST(small_emulated_value % emulated_value == small_emulated_value);
BOOST_TEST(small_emulated_value % small_emulated_value == 0);
Expand Down
Loading