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
6 changes: 0 additions & 6 deletions doc/modules/ROOT/pages/api_reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,6 @@ Listed by analogous STL header.
| xref:config.adoc#no_int128[`BOOST_INT128_NO_BUILTIN_INT128`]
| Disables use of compiler built-in `__int128`

| xref:config.adoc#sign_compare[`BOOST_INT128_ALLOW_SIGN_COMPARE`]
| Allows comparison between signed and unsigned types

| xref:config.adoc#sign_conversion[`BOOST_INT128_ALLOW_SIGN_CONVERSION`]
| Allows implicit sign conversion

| xref:config.adoc#disable_exceptions[`BOOST_INT128_DISABLE_EXCEPTIONS`]
| Disables exception throwing

Expand Down
10 changes: 0 additions & 10 deletions doc/modules/ROOT/pages/config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ Allowed functions have `BOOST_INT128_HOST_DEVICE` as part of their function sign
[#no_int128]
- `BOOST_INT128_NO_BUILTIN_INT128`: The user may define this when they do not want the internal implementations to rely on builtin `pass:[__int128]` or `pass:[unsigned __int128]` types.

[#sign_compare]
- `BOOST_INT128_ALLOW_SIGN_COMPARE`: Allows comparisons between this library's types and built-in types of opposite signedness. Analogous to disabling GCC's `-Wsign-compare` warning.

IMPORTANT: NOT DEFINED BY DEFAULT FOR CORRECTNESS

[#sign_conversion]
- `BOOST_INT128_ALLOW_SIGN_CONVERSION`: Allows arithmetic operations between this library's types and built-in types of opposite signedness. Analogous to disabling GCC's `-Wsign-conversion` warning. Implies `BOOST_INT128_ALLOW_SIGN_COMPARE`.

IMPORTANT: NOT DEFINED BY DEFAULT FOR CORRECTNESS

[#disable_exceptions]
- `BOOST_INT128_DISABLE_EXCEPTIONS`: Allows exceptions to be disabled.
This macro will automatically be defined in the presence of `-fno-exceptions` or similar MSVC flags.
Expand Down
15 changes: 10 additions & 5 deletions doc/modules/ROOT/pages/int128_t.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@ Otherwise, it is left up to the compiler to decide.
[#i128_operator_behavior]
== Operator Behavior

For all the following operators use of unsigned overloads will error from `static_assert` by default.
This is the library's way of enforcing the behavior of `-Wsign-conversion` and `-Wsign-comparison` in a library type.
If you want to compare with unsigned types you must define `BOOST_INT128_ALLOW_SIGN_COMPARE`,
and similarly you must define `BOOST_INT128_ALLOW_SIGN_CONVERSION` for other operations with mixed signedness.
These will both cast the unsigned integer to a signed integer and then perform the operation.
All comparison, arithmetic, bitwise, and shift operators are defined between `int128_t` and any built-in integer type, signed or unsigned.
Their behavior follows the C++ usual arithmetic conversions and is value-identical to the corresponding builtin `__int128` operation.
Specifically:

* For built-in unsigned types of lesser rank (`uint8_t` through `uint64_t`), `int128_t` is the common type and arithmetic / bitwise operations return `int128_t` with signed semantics.
* For `unsigned __int128` (same rank), the signed `int128_t` is converted to the unsigned counterpart, the operation is performed unsigned, and arithmetic / bitwise operations return `uint128_t` (the library's wrapper for `unsigned __int128`).
* 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.

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

[#i128_constructors]
== Constructors
Expand Down
224 changes: 62 additions & 162 deletions doc/modules/ROOT/pages/mixed_type_ops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,47 @@ https://www.boost.org/LICENSE_1_0.txt

The ability to convert between the two types via `static_cast` is available as documented in the above class descriptions.

== Comparisons and Arithmetics
== Operator Overloads Across Types

The following operations are *disabled by default*.
Since we cannot enforce `-Wsign-conversion` and `-Wsign-compare` through the compiler, we instead `static_assert` that the operation is unavailable.
This removes a common source of error (search "Sign Conversion" on Stack Overflow).
All comparison, arithmetic, bitwise, and shift operators are provided across:

To enable these operations, define the appropriate configuration macros before including any library headers:
* `int128_t` and `uint128_t` (cross-type),
* `int128_t` / `uint128_t` and any built-in integer type (signed or unsigned, including the compiler's 128-bit `pass:[__int128]` / `pass:[unsigned __int128]` where supported).

* xref:config.adoc#sign_compare[`BOOST_INT128_ALLOW_SIGN_COMPARE`] - enables comparison operators between signed and unsigned types
* xref:config.adoc#sign_conversion[`BOOST_INT128_ALLOW_SIGN_CONVERSION`] - enables arithmetic operators between signed and unsigned types (implies `BOOST_INT128_ALLOW_SIGN_COMPARE`)
The behavior and return type of every mixed-sign overload follow the C++ usual arithmetic conversions, identical to what the equivalent built-in `pass:[__int128]` / `pass:[unsigned __int128]` operation would produce, including two's-complement wrap-around semantics.

=== Result Type Rules

[cols="3,2,3", options="header"]
|===
| Operands | Common type | Result of arithmetic / bitwise

| `int128_t` and `uint128_t` (same rank, mixed sign)
| `uint128_t`
| `uint128_t`

| `int128_t` and `pass:[unsigned __int128]` (same rank, mixed sign)
| `uint128_t`
| `uint128_t`

| `uint128_t` and `pass:[__int128]` (same rank, mixed sign)
| `uint128_t`
| `uint128_t`

| `int128_t` and a small unsigned built-in (`uint8_t` to `uint64_t`)
| `int128_t`
| `int128_t`

| `uint128_t` and a small signed built-in (`int8_t` to `int64_t`)
| `uint128_t`
| `uint128_t`
|===

For shift operators (`pass:[<<]`, `pass:[>>]`), the result type follows the LHS type regardless of the RHS, matching the built-in shift rules.

For comparison operators (`pass:[==]`, `pass:[!=]`, `pass:[<]`, `pass:[<=]`, `pass:[>]`, `pass:[>=]`), the return type is always `bool` and the comparison is performed on the operands after they have been converted to the common type above.

=== Cross-type Operator Signatures

[source, c++]
----
Expand All @@ -33,193 +64,62 @@ namespace int128 {
//=====================================

BOOST_INT128_HOST_DEVICE constexpr bool operator==(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator==(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator!=(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator!=(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator<(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator<(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator<=(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator<=(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator>(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator>(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator>=(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator>=(int128_t lhs, uint128_t rhs);

//=====================================
// Arithmetic Operators
//=====================================

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator+(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator+(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator-(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator-(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator*(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator*(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator/(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator/(int128_t lhs, uint128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator%(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator%(int128_t lhs, uint128_t rhs);

} // namespace int128
} // namespace boost

----

== Comparisons

If you define xref:config.adoc#sign_compare[`BOOST_INT128_ALLOW_SIGN_COMPARE`], the operators have the following behavior.

=== Equality

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr bool operator==(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator==(int128_t lhs, uint128_t rhs);
----

If the `int128_t` argument is less than 0 returns `false`.
Otherwise, returns the same as `static_cast<uint128_t>(lhs) == static_cast<uint128_t>(rhs)`.

=== Inequality

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr bool operator!=(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator!=(int128_t lhs, uint128_t rhs);
----

If the `int128_t` argument is less than 0 returns `true`.
Otherwise, returns the same as `static_cast<uint128_t>(lhs) != static_cast<uint128_t>(rhs)`.

=== Less Than

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr bool operator<(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator<(int128_t lhs, uint128_t rhs);
----

If `lhs` is type `int128_t` returns `true` if `lhs < 0`
If `rhs` is type `int128_t` returns `false` if `rhs < 0`
Otherwise, returns the same as `static_cast<uint128_t>(lhs) < static_cast<uint128_t>(rhs)`.

=== Less Than or Equal To

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr bool operator<=(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator<=(int128_t lhs, uint128_t rhs);
----

If `lhs` is type `int128_t` returns `true` if `lhs < 0`
If `rhs` is type `int128_t` returns `false` if `rhs < 0`
Otherwise, returns the same as `static_cast<uint128_t>(lhs) pass:[<=] static_cast<uint128_t>(rhs)`.

=== Greater Than

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr bool operator>(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator>(int128_t lhs, uint128_t rhs);
----

If `lhs` is type `int128_t` returns `false` if `lhs < 0`
If `rhs` is type `int128_t` returns `true` if `rhs < 0`
Otherwise, returns the same as `static_cast<uint128_t>(lhs) > static_cast<uint128_t>(rhs)`.

=== Greater Than or Equal To

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr bool operator>=(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr bool operator>=(int128_t lhs, uint128_t rhs);
----

If `lhs` is type `int128_t` returns `false` if `lhs < 0`
If `rhs` is type `int128_t` returns `true` if `rhs < 0`
Otherwise, returns the same as `static_cast<uint128_t>(lhs) pass:[>=] static_cast<uint128_t>(rhs)`.

== Arithmetic

If you define xref:config.adoc#sign_conversion[`BOOST_INT128_ALLOW_SIGN_CONVERSION`], the operators have the following behavior.

=== Addition

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator+(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator+(int128_t lhs, uint128_t rhs);
----

Returns the same as `static_cast<uint128_t>(lhs) + static_cast<uint128_t>(rhs)`

=== Subtraction

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator-(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator-(int128_t lhs, uint128_t rhs);
----

Returns the same as `static_cast<uint128_t>(lhs) - static_cast<uint128_t>(rhs)`

=== Multiplication

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator*(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator*(int128_t lhs, uint128_t rhs);
----
//=====================================
// Bitwise Operators
//=====================================

Returns the same as `static_cast<uint128_t>(lhs) * static_cast<uint128_t>(rhs)`
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator|(uint128_t lhs, int128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator|(int128_t lhs, uint128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator&(uint128_t lhs, int128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator&(int128_t lhs, uint128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator^(uint128_t lhs, int128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator^(int128_t lhs, uint128_t rhs);

=== Division
//=====================================
// Shift Operators
//=====================================
// Result type follows the LHS.

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator/(uint128_t lhs, int128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr int128_t operator<<(int128_t lhs, uint128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator<<(uint128_t lhs, int128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr int128_t operator>>(int128_t lhs, uint128_t rhs);
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator>>(uint128_t lhs, int128_t rhs);

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator/(int128_t lhs, uint128_t rhs);
} // namespace int128
} // namespace boost
----

Returns the same as `static_cast<uint128_t>(lhs) / static_cast<uint128_t>(rhs)`

=== Modulo

[source, c++]
----
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator%(uint128_t lhs, int128_t rhs);
The cross-type arithmetic and bitwise operators return the same value as `static_cast<uint128_t>(lhs) op static_cast<uint128_t>(rhs)`.
The comparison operators return the same value as that expression compared with the matching unsigned operator.

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator%(int128_t lhs, uint128_t rhs);
----
=== Operations with built-in `pass:[__int128]` / `pass:[unsigned __int128]`

Returns the same as `static_cast<uint128_t>(lhs) % static_cast<uint128_t>(rhs)`
When the compiler provides 128-bit built-in integer types, all of the operators above are also available between a library type and the built-in type of opposite signedness (e.g. `uint128_t op pass:[__int128]`, `pass:[unsigned __int128] op int128_t`).
The result type follows the same rules as the table above (`uint128_t` for arithmetic and bitwise; the LHS type for shifts; `bool` for comparisons), and the produced value is identical to what an all-built-in computation would yield.
6 changes: 3 additions & 3 deletions doc/modules/ROOT/pages/overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ The types provided by the library also natively support running on GPUs using CU

== Use Cases

* Networking IPv6 addresses are 128 bits wide; a single integer makes masking, comparison, and arithmetic straightforward.
* Unique identifiers UUIDs / GUIDs are 128-bit values commonly used as database keys and distributed system identifiers.
* Scientific and Financial computing Extended-range accumulators, large combinatorial values, and algorithms that need overflow-free 64x64 multiplication.
* Networking: IPv6 addresses are 128 bits wide; a single integer makes masking, comparison, and arithmetic straightforward.
* Unique identifiers: UUIDs / GUIDs are 128-bit values commonly used as database keys and distributed system identifiers.
* Scientific and Financial computing: Extended-range accumulators, large combinatorial values, and algorithms that need overflow-free 64x64 multiplication.

== Supported Compilers

Expand Down
49 changes: 8 additions & 41 deletions doc/modules/ROOT/pages/uint128_t.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -62,50 +62,17 @@ Otherwise, it is left up to the compiler to decide.
[#u128_operator_behavior]
== Operator Behavior

For all the following operators use of signed overloads will error from `static_assert` by default.
This is the library's way of enforcing the behavior of `-Wsign-conversion` and `-Wsign-comparison` in a library type.
If you want to compare with signed types you must define `BOOST_INT128_ALLOW_SIGN_COMPARE`,
and similarly you must define `BOOST_INT128_ALLOW_SIGN_CONVERSION` for other operations with mixed signedness.
These will both cast the signed integer to an unsigned integer and then perform the operation.
All comparison, arithmetic, bitwise, and shift operators are defined between `uint128_t` and any built-in integer type, signed or unsigned.
Their behavior follows the C++ usual arithmetic conversions and is value-identical to the corresponding builtin `unsigned __int128` operation.
Specifically:

=== Sign Compare Behavior Deviation
* The signed operand (whether a small `intN_t` or `__int128`) is converted to `uint128_t` (sign-extended for negatives), and the operation is performed unsigned with two's-complement wrap-around semantics. Arithmetic and bitwise operations return `uint128_t`.
* For shift operators, the result type follows the LHS: `uint128_t << T` and `uint128_t >> T` always return `uint128_t`, regardless of `T`.
* All comparison operators return `bool`, with the comparison performed on the operands after conversion to the common type.

The behavior of `uint128_t` will deviate from the behavior of builtin unsigned integers with mixed sign comparisons in hopefully a less surprising way.
A built-in sign compare looks something like
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.

[source, c++]
----
template <typename UnsignedInteger, typename SignedInteger>
constexpr bool operator>(const UnsignedInteger lhs, const SignedInteger rhs)
{
return lhs > static_cast<UnsignedInteger>(rhs)
}
----

If you were to call this function with arguments 5U and -5, you would get the surprising answer of `false`.
Why?
The two's complement representation of -5 has its most significant bit set (along with many other high bits).
When cast to unsigned, this bit pattern is reinterpreted as a huge positive number, far greater than 5.

With `uint128_t` we have checks even in this case like so:

[source, c++]
----
template <typename SignedInteger>
constexpr bool operator>(const uint128_t lhs, const SignedInteger rhs) noexcept
{
if (rhs >= 0)
{
return lhs > static_cast<uint128_t>(rhs);
}
else
{
return true;
}
}
----

This allows the library to return the correct answer even when mixing signs.
See xref:mixed_type_ops.adoc[Mixed Type Operations] for the full set of cross-type signatures and detailed result-type rules.

[#u128_constructors]
== Constructors
Expand Down
Loading
Loading