[libcxx-commits] [libcxx] linear_congruential_engine: add using more precision to prevent overflow (PR #81583)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Apr 13 16:59:25 PDT 2024


================
@@ -80,21 +97,40 @@ struct __lce_ta<__a, 0, __m, (unsigned long long)(~0), true> {
 };
 
 template <unsigned long long __a, unsigned long long __c, unsigned long long __m>
-struct __lce_ta<__a, __c, __m, (unsigned long long)(~0), false> {
+struct __lce_ta<__a, __c, __m, (unsigned long long)(~0), 1> {
+  typedef unsigned long long result_type;
+  _LIBCPP_HIDE_FROM_ABI static result_type next(result_type __x) {
+    // Use (((a*x) % m) + c) % m
+    __x = (__a * __x) % __m;
+    __x += __c - (__x >= __m - __c) * __m;
+    return __x;
+  }
+};
+
+template <unsigned long long __a, unsigned long long __c, unsigned long long __m>
+struct __lce_ta<__a, __c, __m, (unsigned long long)(~0), 0> {
   typedef unsigned long long result_type;
   _LIBCPP_HIDE_FROM_ABI static result_type next(result_type __x) { return (__a * __x + __c) % __m; }
 };
 
 template <unsigned long long __a, unsigned long long __c>
-struct __lce_ta<__a, __c, 0, (unsigned long long)(~0), false> {
+struct __lce_ta<__a, __c, 0ull, (unsigned long long)(~0), 0> {
   typedef unsigned long long result_type;
   _LIBCPP_HIDE_FROM_ABI static result_type next(result_type __x) { return __a * __x + __c; }
 };
 
 // 32
 
+template <unsigned long long __a, unsigned long long __c, unsigned long long __m>
+struct __lce_ta<__a, __c, __m, unsigned(~0), 3> {
+  typedef unsigned result_type;
+  _LIBCPP_HIDE_FROM_ABI static result_type next(result_type __x) {
+    return static_cast<result_type>(__lce_ta<__a, __c, __m, (unsigned long long)(~0)>::next(__x));
----------------
LRFLEW wrote:

I was thinking this looked weird, but this is what was there before. I agree it looks wrong (even if it technically isn't), though. My issue with this change is that it can't *quite* be applied universally. Even if I changed this in all of the specializations of `__lce_ta`, the definition of `linear_congruential_engine::_Mp` would still have that issue, as it's currently defined as `result_type(~0)`.

Personally, I'd rather change this line to `~(unsigned long long)(0)`, as I think it's clearer what the intent is, and is less prone to mistakes from using the wrong suffix.

https://github.com/llvm/llvm-project/pull/81583


More information about the libcxx-commits mailing list