Skip to content
Open
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: 2 additions & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*** xref:api_reference.adoc#api_cstdlib[`<cstdlib>`]
*** xref:api_reference.adoc#api_charconv[`<charconv>`]
*** xref:api_reference.adoc#api_cmath[`<cmath>`]
*** xref:api_reference.adoc#api_functional[`<functional>`]
*** xref:api_reference.adoc#api_iostream[`<iostream>`]
*** xref:api_reference.adoc#api_ios[`<ios>`]
*** xref:api_reference.adoc#api_numeric[`<numeric>`]
Expand Down Expand Up @@ -54,6 +55,7 @@
* xref:cstdlib.adoc[]
* xref:charconv.adoc[]
* xref:stream.adoc[]
* xref:hash.adoc[]
* xref:numeric.adoc[]
* xref:string.adoc[]
* xref:utilities.adoc[]
Expand Down
23 changes: 23 additions & 0 deletions doc/modules/ROOT/pages/api_reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ https://www.boost.org/LICENSE_1_0.txt
| https://en.cppreference.com/w/cpp/types/numeric_limits[`std::numeric_limits<int128_t>`]
| Numeric limits specialization for `int128_t`

| xref:hash.adoc[`std::hash<uint128_t>`]
| Hash specialization for `uint128_t`

| xref:hash.adoc[`std::hash<int128_t>`]
| Hash specialization for `int128_t`

| xref:cstdlib.adoc#div_structs[`u128div_t`]
| Result type for `div(uint128_t, uint128_t)`

Expand Down Expand Up @@ -164,6 +170,20 @@ Listed by analogous STL header.
| Computes quotient and remainder simultaneously
|===

[#api_functional]
=== xref:hash.adoc[`<functional>`]

[cols="1,2", options="header"]
|===
| Specialization | Description

| xref:hash.adoc[`std::hash<uint128_t>`]
| Enables `uint128_t` as a key in unordered associative containers

| xref:hash.adoc[`std::hash<int128_t>`]
| Enables `int128_t` as a key in unordered associative containers
|===

[#api_formatting]
=== xref:format.adoc[Formatting]

Expand Down Expand Up @@ -367,6 +387,9 @@ Listed by analogous STL header.
| xref:format.adoc#std_format[`<boost/int128/format.hpp>`]
| Formatting integration for pass:[C++20] `<format>`

| xref:hash.adoc[`<boost/int128/hash.hpp>`]
| `std::hash` specializations for `int128_t` and `uint128_t`

| `<boost/int128/int128.hpp>`
| The xref:uint128_t.adoc[`uint128_t`] and xref:int128_t.adoc[`int128_t`] types

Expand Down
3 changes: 3 additions & 0 deletions doc/modules/ROOT/pages/file_structure.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ The entire library can be consumed via `<boost/int128.hpp>`, or by independently
| xref:format.adoc[`<boost/int128/format.hpp>`]
| C++20 `std::format` support

| xref:hash.adoc[`<boost/int128/hash.hpp>`]
| `std::hash` specializations for `int128_t` and `uint128_t`

| `<boost/int128/int128.hpp>`
| Core type definitions (`uint128_t`, `int128_t`)

Expand Down
69 changes: 69 additions & 0 deletions doc/modules/ROOT/pages/hash.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
////
Copyright 2026 Matt Borland
Distributed under the Boost Software License, Version 1.0.
https://www.boost.org/LICENSE_1_0.txt
////

[#hash]
= Hashing
:idprefix: hash_

The `<boost/int128/hash.hpp>` header provides specializations of `std::hash` for `uint128_t` and `int128_t`, allowing the library types to be used as keys in `std::unordered_map`, `std::unordered_set`, and any other container that relies on `std::hash`.

[source, c++]
----
#include <boost/int128/hash.hpp>
----

[#hash_specializations]
== Specializations

[source, c++]
----
namespace std {

template <>
struct hash<boost::int128::int128_t>
{
std::size_t operator()(boost::int128::int128_t v) const noexcept;
};

template <>
struct hash<boost::int128::uint128_t>
{
std::size_t operator()(boost::int128::uint128_t v) const noexcept;
};

} // namespace std
----

Each 64-bit half of the value is first run through a SplitMix64 finalizer so that every input bit influences the lower bits of the result.
This is necessary because `std::hash<std::uint64_t>` is permitted to truncate to `std::size_t`, which would lose the upper 32 bits on 32-bit platforms and cause distinct 128-bit values to collide.
The two finalized halves are then combined with the `boost::hash_combine` mixing formula.

[#hash_guarantees]
== Guarantees

* Two values comparing equal under `operator==` produce the same hash.
* For any non-zero `v`, `std::hash<int128_t>{}(v) != std::hash<int128_t>{}(-v)`.
* The mixing function is asymmetric, so `{high, low}` and `{low, high}` do not collide except by chance.
* The hash value is implementation-defined and may differ across platforms, compilers, or library versions. Do not persist hash values across runs.

[#hash_example]
== Example

[source, c++]
----
#include <boost/int128/int128.hpp>
#include <boost/int128/hash.hpp>
#include <unordered_map>

int main()
{
std::unordered_map<boost::int128::uint128_t, int> counts {};
counts[boost::int128::uint128_t{1, 0}] = 1;
counts[boost::int128::uint128_t{0, 1}] = 2;

return 0;
}
----
1 change: 1 addition & 0 deletions include/boost/int128.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
#include <boost/int128/cstdlib.hpp>
#include <boost/int128/string.hpp>
#include <boost/int128/utilities.hpp>
#include <boost/int128/hash.hpp>

#endif // BOOST_INT128_HPP
68 changes: 68 additions & 0 deletions include/boost/int128/hash.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#ifndef BOOST_INT128_HASH_HPP
#define BOOST_INT128_HASH_HPP

#include <boost/int128/int128.hpp>

#ifndef BOOST_INT128_BUILD_MODULE

#include <cstddef>
#include <cstdint>
#include <functional>

#endif

namespace boost {
namespace int128 {
namespace detail {

// splitmix64 finalizer: mixes all 64 input bits into the result before any narrowing to size_t.
// This is required for correctness on platforms where size_t is 32 bits
inline std::size_t hash_finalize_64(std::uint64_t v) noexcept
{
v ^= v >> 30;
v *= UINT64_C(0xbf58476d1ce4e5b9);
v ^= v >> 27;
v *= UINT64_C(0x94d049bb133111eb);
v ^= v >> 31;
return v;
}

} // namespace detail
} // namespace int128
} // namespace boost

namespace std {

template <>
struct hash<boost::int128::int128_t>
{
auto operator()(const boost::int128::int128_t v) const noexcept -> std::size_t
{
const std::size_t low_hash {boost::int128::detail::hash_finalize_64(v.low)};
const std::size_t high_hash {boost::int128::detail::hash_finalize_64(static_cast<std::uint64_t>(v.high))};

// boost::hash_combine style mixing of the two finalized halves
return low_hash ^ (high_hash + static_cast<std::size_t>(0x9e3779b9) + (low_hash << 6) + (low_hash >> 2));
}
};

template <>
struct hash<boost::int128::uint128_t>
{
auto operator()(const boost::int128::uint128_t v) const noexcept -> std::size_t
{
const std::size_t low_hash {boost::int128::detail::hash_finalize_64(v.low)};
const std::size_t high_hash {boost::int128::detail::hash_finalize_64(v.high)};

// boost::hash_combine style mixing of the two finalized halves
return low_hash ^ (high_hash + static_cast<std::size_t>(0x9e3779b9) + (low_hash << 6) + (low_hash >> 2));
}
};

} // namespace std

#endif // BOOST_INT128_HASH_HPP
2 changes: 2 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ run test_div.cpp ;
run test_num_digits.cpp ;
run test_spaceship_operator.cpp ;
run test_to_string.cpp ;
run test_hash.cpp ;

# Make sure we run the examples as well
run ../examples/construction.cpp ;
Expand Down Expand Up @@ -123,6 +124,7 @@ compile compile_tests/charconv_compile.cpp ;
compile compile_tests/climits_compile.cpp ;
compile compile_tests/cstdlib_compile.cpp ;
compile compile_tests/format_compile.cpp ;
compile compile_tests/hash_compile.cpp ;
compile compile_tests/int128_compile.cpp ;
compile compile_tests/iostream_compile.cpp ;
compile compile_tests/limits_compile.cpp ;
Expand Down
10 changes: 10 additions & 0 deletions test/compile_tests/hash_compile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/int128/hash.hpp>

int main()
{
return 0;
}
Loading
Loading