
This fixes two major mistakes in the implementation of `linear_congruential_engine` that allowed it to produce incorrect output. Specifically, these mistakes are in `__lce_alg_picker`, which is used to determine whether Schrage's algorithm is valid and needed. The first mistake is in the definition of `_OverflowOK`. The code comment and the description of [D65041](https://reviews.llvm.org/D65041) both indicate that it's supposed to be true iff `m` is a power of two. However, the definition used does not work out to that, and instead is true whenever `m` is even. This could result in `linear_congruential_engine` using an invalid implementation, as it would incorrectly assume that any integer overflow can't change the result. I changed the implementation to one that accurately checks if `m` is a power of two. Technically, this implementation has an edge case where it considers `0` to be a power of two, but in this case this is actually accurate behavior, as `m = 0` indicates a modulus of 2^w where w is the size of `result_type` in bits, which *is* a power of two. The second mistake is in the static assert. The original static assert erroneously included an unnecessary `a != 0 || m != 0`. Combined with the `|| !_MightOverflow`, this actually resulted in the static assert being impossible to fail. Applying De Morgan's law and expanding `_MightOverflow` gives that the only way this static assert can be triggered is if `a == 0 && m == 0 && a != 0 && m != 0 && ...`, which clearly cannot be true. I simply removed the explicit checks against `a` and `m`, as the intended checks are already included in `_MightOverflow` and `_SchrageOK`, and their inclusion doesn't provide any obvious semantic benefit. This should fix all the current instances where `linear_congruential_engine` uses an invalid implementation. This technically isn't a complete implementation, though, since the static assert will cause some instantiations of `linear_congruential_engine` not disallowed by the standard from compiling. However, this should still be an improvement, as all compiling instantiations of `linear_congruential_engine` should use a valid implementation. Fixing the cases where the static assert triggers will require adding additional implementations, some of which will be fairly non-trivial, so I'd rather leave those for another PR so they don't hold up these more important fixes. Fixes #33554
82 lines
1.8 KiB
C++
82 lines
1.8 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// <random>
|
|
|
|
// template <class UIntType, UIntType a, UIntType c, UIntType m>
|
|
// class linear_congruential_engine;
|
|
|
|
// linear_congruential_engine();
|
|
|
|
#include <random>
|
|
#include <cassert>
|
|
|
|
#include "test_macros.h"
|
|
|
|
template <class T, T a, T c, T m>
|
|
void
|
|
test1()
|
|
{
|
|
typedef std::linear_congruential_engine<T, a, c, m> LCE;
|
|
LCE e1;
|
|
LCE e2;
|
|
e2.seed();
|
|
assert(e1 == e2);
|
|
}
|
|
|
|
template <class T>
|
|
void
|
|
test()
|
|
{
|
|
const int W = sizeof(T) * CHAR_BIT;
|
|
const T M(static_cast<T>(-1));
|
|
const T A(static_cast<T>((static_cast<T>(1) << (W / 2)) - 1));
|
|
|
|
// Cases where m = 0
|
|
test1<T, 0, 0, 0>();
|
|
test1<T, A, 0, 0>();
|
|
test1<T, 0, 1, 0>();
|
|
test1<T, A, 1, 0>();
|
|
|
|
// Cases where m = 2^n for n < w
|
|
test1<T, 0, 0, 256>();
|
|
test1<T, 5, 0, 256>();
|
|
test1<T, 0, 1, 256>();
|
|
test1<T, 5, 1, 256>();
|
|
|
|
// Cases where m is odd and a = 0
|
|
test1<T, 0, 0, M>();
|
|
test1<T, 0, M - 2, M>();
|
|
test1<T, 0, M - 1, M>();
|
|
|
|
// Cases where m is odd and m % a <= m / a (Schrage)
|
|
test1<T, A, 0, M>();
|
|
test1<T, A, M - 2, M>();
|
|
test1<T, A, M - 1, M>();
|
|
|
|
/*
|
|
// Cases where m is odd and m % a > m / a (not implemented)
|
|
test1<T, M - 2, 0, M>();
|
|
test1<T, M - 2, M - 2, M>();
|
|
test1<T, M - 2, M - 1, M>();
|
|
test1<T, M - 1, 0, M>();
|
|
test1<T, M - 1, M - 2, M>();
|
|
test1<T, M - 1, M - 1, M>();
|
|
*/
|
|
}
|
|
|
|
int main(int, char**)
|
|
{
|
|
test<unsigned short>();
|
|
test<unsigned int>();
|
|
test<unsigned long>();
|
|
test<unsigned long long>();
|
|
|
|
return 0;
|
|
}
|