diff options
author | Giovanni Bajo <rasky@develer.com> | 2024-07-31 20:03:40 +0100 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2024-08-23 13:39:35 +0100 |
commit | 125bab23ad75449333983c9389898c5b92b3aa0d (patch) | |
tree | 8e23627fde174edc91c061007fa870c0d8dc9dd0 /libstdc++-v3/include | |
parent | de1923f9f4d5344694c22ca883aeb15caf635734 (diff) | |
download | gcc-125bab23ad75449333983c9389898c5b92b3aa0d.zip gcc-125bab23ad75449333983c9389898c5b92b3aa0d.tar.gz gcc-125bab23ad75449333983c9389898c5b92b3aa0d.tar.bz2 |
libstdc++: Fix std::random_shuffle for low RAND_MAX [PR88935]
When RAND_MAX is small and the number of elements being shuffled is
close to it, we get very uneven distributions in std::random_shuffle.
This uses a simple xorshift generator seeded by std::rand if we can't
rely on std::rand itself.
libstdc++-v3/ChangeLog:
PR libstdc++/88935
* include/bits/stl_algo.h (random_shuffle) [RAND_MAX < INT_MAX]:
Use xorshift instead of rand().
* testsuite/25_algorithms/random_shuffle/88935.cc: New test.
Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Giovanni Bajo <rasky@develer.com>
Diffstat (limited to 'libstdc++-v3/include')
-rw-r--r-- | libstdc++-v3/include/bits/stl_algo.h | 42 |
1 files changed, 33 insertions, 9 deletions
diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h index 541f588..778a37a 100644 --- a/libstdc++-v3/include/bits/stl_algo.h +++ b/libstdc++-v3/include/bits/stl_algo.h @@ -4521,15 +4521,39 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO _RandomAccessIterator>) __glibcxx_requires_valid_range(__first, __last); - if (__first != __last) - for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i) - { - // XXX rand() % N is not uniformly distributed - _RandomAccessIterator __j = __first - + std::rand() % ((__i - __first) + 1); - if (__i != __j) - std::iter_swap(__i, __j); - } + if (__first == __last) + return; + +#if RAND_MAX < __INT_MAX__ + if (__builtin_expect((__last - __first) >= RAND_MAX / 4, 0)) + { + // Use a xorshift implementation seeded by two calls to rand() + // instead of using rand() for all the random numbers needed. + unsigned __xss + = (unsigned)std::rand() ^ ((unsigned)std::rand() << 15); + for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i) + { + __xss += !__xss; + __xss ^= __xss << 13; + __xss ^= __xss >> 17; + __xss ^= __xss << 5; + _RandomAccessIterator __j = __first + + (__xss % ((__i - __first) + 1)); + if (__i != __j) + std::iter_swap(__i, __j); + } + return; + } +#endif + + for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i) + { + // XXX rand() % N is not uniformly distributed + _RandomAccessIterator __j = __first + + (std::rand() % ((__i - __first) + 1)); + if (__i != __j) + std::iter_swap(__i, __j); + } } /** |