[gcc r16-8683] libstdc++: Fix philox_engine counter increment carry
Jonathan Wakely
redi@gcc.gnu.org
Wed Apr 15 16:54:12 GMT 2026
https://gcc.gnu.org/g:1bb8dc29aab17d29a7ab10ffb3e96900b580671a
commit r16-8683-g1bb8dc29aab17d29a7ab10ffb3e96900b580671a
Author: Elena Tyuleneva <elena.tyuleneva@intel.com>
Date: Wed Apr 15 15:54:51 2026 +0100
libstdc++: Fix philox_engine counter increment carry
The philox counter increment that happens in
philox_engine::_M_transition(), applies +1 to _M_x[0] before the bitwise
OR with (_M_x[1] << __w), so the carry from _M_x[0] is merged into the
_M_x[1] portion rather than being added to it. This causes the counter
to cycle prematurely and never advance past {0, 0, 1, 2^w - 1}.
Fix by changing operations order, which results in forming the
full-width value from _M_x[1]:_M_x[0] and then adding 1 on both the n ==
4 and n == 2 paths.
libstdc++-v3/ChangeLog:
* include/bits/random.tcc (philox_engine::_M_transtiion): Fix
counter increment to propagate carry correctly.
* testsuite/26_numerics/random/philox_engine/operators/counter_carry.cc:
New test.
Signed-off-by: Elena Tyuleneva <elena.tyuleneva@intel.com>
Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
Reviewed-by: Patrick Palka <ppalka@redhat.com>
Diff:
---
libstdc++-v3/include/bits/random.tcc | 10 +--
.../philox_engine/operators/counter_carry.cc | 81 ++++++++++++++++++++++
2 files changed, 87 insertions(+), 4 deletions(-)
diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc
index bc3393d35a89..4475e365c2d7 100644
--- a/libstdc++-v3/include/bits/random.tcc
+++ b/libstdc++-v3/include/bits/random.tcc
@@ -945,7 +945,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
__type __uh
= (static_cast<__type>(_M_x[1]) << __w)
- | (static_cast<__type>(_M_x[0]) + 1);
+ | static_cast<__type>(_M_x[0]);
+ ++__uh;
__type __lh
= (static_cast<__type>(_M_x[3]) << __w)
| static_cast<__type>(_M_x[2]);
@@ -963,9 +964,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
else
{
- __type __num =
- (static_cast<__type>(_M_x[1]) << __w)
- | (static_cast<__type>(_M_x[0]) + 1);
+ __type __num
+ = (static_cast<__type>(_M_x[1]) << __w)
+ | static_cast<__type>(_M_x[0]);
+ ++__num;
_M_x[0] = static_cast<_UIntType>(__num & max());
_M_x[1] = static_cast<_UIntType>((__num >> __w) & max());
}
diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/counter_carry.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/counter_carry.cc
new file mode 100644
index 000000000000..a0270eedb5d0
--- /dev/null
+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/counter_carry.cc
@@ -0,0 +1,81 @@
+// { dg-do run { target c++26 } }
+// N5014 29.5.4.5 Class Template philox_engine
+
+#include <random>
+#include <array>
+
+#include <testsuite_hooks.h>
+
+// Helper: set counter, skip one block (n outputs), then compare
+// the next block against a reference engine at the expected counter.
+template<typename Engine, std::size_t N>
+void
+verify_carry(const std::array<typename Engine::result_type, N>& current_counter,
+ const std::array<typename Engine::result_type, N>& expected_incremented_counter)
+{
+ Engine eng_a;
+ eng_a.set_counter(current_counter);
+ // Skip the block produced from current_counter
+ eng_a.discard(N);
+
+ Engine eng_b;
+ eng_b.set_counter(expected_incremented_counter);
+ // Both engines should now produce identical output
+ for (std::size_t i = 0; i < N; ++i)
+ VERIFY( eng_a() == eng_b() );
+}
+
+int main()
+{
+ // philox4x32 (n=4, w=32)
+ verify_carry<std::philox4x32, 4>(std::array<std::philox4x32::result_type, 4>{0, 0, 0, 5},
+ std::array<std::philox4x32::result_type, 4>{0, 0, 0, 6});
+ verify_carry<std::philox4x32, 4>(std::array<std::philox4x32::result_type, 4>{0, 0, 0, 0xFFFFFFFF},
+ std::array<std::philox4x32::result_type, 4>{0, 0, 1, 0});
+ verify_carry<std::philox4x32, 4>(std::array<std::philox4x32::result_type, 4>{0, 0, 1, 0xFFFFFFFF},
+ std::array<std::philox4x32::result_type, 4>{0, 0, 2, 0});
+ verify_carry<std::philox4x32, 4>(std::array<std::philox4x32::result_type, 4>{0, 0, 0xFFFFFFFF, 0xFFFFFFFF},
+ std::array<std::philox4x32::result_type, 4>{0, 1, 0, 0});
+ verify_carry<std::philox4x32, 4>(std::array<std::philox4x32::result_type, 4>{0, 1, 0xFFFFFFFF, 0xFFFFFFFF},
+ std::array<std::philox4x32::result_type, 4>{0, 2, 0, 0});
+ verify_carry<std::philox4x32, 4>(std::array<std::philox4x32::result_type, 4>{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ std::array<std::philox4x32::result_type, 4>{0, 0, 0, 0});
+
+ // philox4x64 (n=4, w=64)
+ constexpr auto philox4x64_max = std::philox4x64::max();
+ verify_carry<std::philox4x64, 4>(std::array<std::philox4x64::result_type, 4>{0, 0, 0, philox4x64_max},
+ std::array<std::philox4x64::result_type, 4>{0, 0, 1, 0});
+ verify_carry<std::philox4x64, 4>(std::array<std::philox4x64::result_type, 4>{0, 0, 1, philox4x64_max},
+ std::array<std::philox4x64::result_type, 4>{0, 0, 2, 0});
+ verify_carry<std::philox4x64, 4>(std::array<std::philox4x64::result_type, 4>{0, 0, philox4x64_max, philox4x64_max},
+ std::array<std::philox4x64::result_type, 4>{0, 1, 0, 0});
+ verify_carry<std::philox4x64, 4>(std::array<std::philox4x64::result_type, 4>{0, 1, philox4x64_max, philox4x64_max},
+ std::array<std::philox4x64::result_type, 4>{0, 2, 0, 0});
+ verify_carry<std::philox4x64, 4>(std::array<std::philox4x64::result_type, 4>{philox4x64_max, philox4x64_max, philox4x64_max, philox4x64_max},
+ std::array<std::philox4x64::result_type, 4>{0, 0, 0, 0});
+
+ // philox2x32 (n=2, w=32)
+ using philox2x32 = std::philox_engine<std::uint_fast32_t,
+ 32, 2, 10, 0xCD9E8D57, 0x9E3779B9>;
+ verify_carry<philox2x32, 2>(std::array<philox2x32::result_type, 2>{0, 5},
+ std::array<philox2x32::result_type, 2>{0, 6});
+ verify_carry<philox2x32, 2>(std::array<philox2x32::result_type, 2>{0, 0xFFFFFFFF},
+ std::array<philox2x32::result_type, 2>{1, 0});
+ verify_carry<philox2x32, 2>(std::array<philox2x32::result_type, 2>{1, 0xFFFFFFFF},
+ std::array<philox2x32::result_type, 2>{2, 0});
+ verify_carry<philox2x32, 2>(std::array<philox2x32::result_type, 2>{0xFFFFFFFF, 0xFFFFFFFF},
+ std::array<philox2x32::result_type, 2>{0, 0});
+
+ // philox2x64 (n=2, w=64)
+ using philox2x64 = std::philox_engine<std::uint_fast64_t,
+ 64, 2, 10, 0xCD9E8D57, 0x9E3779B9>;
+ constexpr auto philox2x64_max = philox2x64::max();
+ verify_carry<philox2x64, 2>(std::array<philox2x64::result_type, 2>{0, 5},
+ std::array<philox2x64::result_type, 2>{0, 6});
+ verify_carry<philox2x64, 2>(std::array<philox2x64::result_type, 2>{0, philox2x64_max},
+ std::array<philox2x64::result_type, 2>{1, 0});
+ verify_carry<philox2x64, 2>(std::array<philox2x64::result_type, 2>{1, philox2x64_max},
+ std::array<philox2x64::result_type, 2>{2, 0});
+ verify_carry<philox2x64, 2>(std::array<philox2x64::result_type, 2>{philox2x64_max, philox2x64_max},
+ std::array<philox2x64::result_type, 2>{0, 0});
+}
More information about the Libstdc++-cvs
mailing list