]> mj.ucw.cz Git - libucw.git/commitdiff
Random: Use full 64bit output from xoroshiro128+, but prefer the higher bits.
authorPavel Charvat <pchar@ucw.cz>
Tue, 9 Jun 2020 10:44:26 +0000 (12:44 +0200)
committerPavel Charvat <pchar@ucw.cz>
Tue, 22 Mar 2022 18:06:59 +0000 (18:06 +0000)
ucw/random-fast.c

index 9404abdcdcc5f7cd753261d6178156aef833ebf6..5c8bfaab5a0c87a50c51a78662b4937e95f841de 100644 (file)
 // Scrambler: 1 (+), 2 (++), 3 (**)
 #define FASTRAND_XOROSHIRO_SCRAMBLER 1
 
-// If defined, we only use the highest 32 bits from each 64 bit number;
-// useful especially for the weakest '+' scrambler.
-// XXX: We could instead cut less bits or use full 64 bits and prefer
-// the higher ones only where trivial, for example in 32 bit fastrand_max().
-#define FASTRAND_XOROSHIRO_ONLY_HIGHER
+// If defined, we only use the highest 60 bits from each 64 bit number;
+// useful especially for the weakest '+' scrambler. Even if not
+// we prefer higher bits where trivial.
+#undef FASTRAND_XOROSHIRO_ONLY_HIGHER
 
 /**
  * FASTRAND_USE_LIBC_SVID
@@ -174,9 +173,10 @@ static inline u32 fastrand_one(struct fastrand *c)
 
 #ifdef FASTRAND_USE_XOROSHIRO
 #ifdef FASTRAND_XOROSHIRO_ONLY_HIGHER
-#  define FASTRAND_ONE_BITS 32
+#  define FASTRAND_ONE_BITS 60
 #else
 #  define FASTRAND_ONE_BITS 64
+#  define FASTRAND_PREFER_HIGHER
 #endif
 
 static void fastrand_init(struct fastrand *c)
@@ -190,13 +190,7 @@ static void fastrand_init(struct fastrand *c)
     (uintmax_t)c->seed_value, FASTRAND_XOROSHIRO_SIZE, FASTRAND_XOROSHIRO_SCRAMBLER);
 }
 
-static inline
-#ifdef FASTRAND_XOROSHIRO_ONLY_HIGHER
-u32
-#else
-u64
-#endif
-fastrand_one(struct fastrand *c)
+static inline u64 fastrand_one(struct fastrand *c)
 {
   u64 result;
   u64 *s = c->xoroshiro_state;
@@ -241,7 +235,7 @@ fastrand_one(struct fastrand *c)
 #endif
 
 #ifdef FASTRAND_XOROSHIRO_ONLY_HIGHER
-  result >>= 32;
+  result >>= 4;
 #endif
   return result;
 }
@@ -361,10 +355,19 @@ COMPILE_ASSERT(fastrand_one_range, FASTRAND_ONE_BITS >= 32 && FASTRAND_ONE_BITS
 #define FASTRAND_ONE_MAX (UINT64_MAX >> (64 - FASTRAND_ONE_BITS))
 #endif
 
+static inline u32 fastrand_u32_helper(struct fastrand *c)
+{
+#if FASTRAND_ONE_BITS > 32 && defined(FASTRAND_PREFER_HIGHER)
+  return fastrand_one(c) >> (FASTRAND_ONE_BITS - 32);
+#else
+  return fastrand_one(c);
+#endif
+}
+
 u32 fastrand_u32(struct fastrand *c)
 {
   FASTRAND_CHECK_SEED(c);
-  return fastrand_one(c);
+  return fastrand_u32_helper(c);
 }
 
 u64 fastrand_u64(struct fastrand *c)
@@ -450,7 +453,7 @@ static inline u32 fastrand_max_u32_helper(struct fastrand *c, u32 bound)
 #if FASTRAND_MAX_U32_ALGORITHM == 1
   while (1)
     {
-      u32 r = fastrand_one(c);
+      u32 r = fastrand_u32_helper(c);
       u32 x = r % bound;
       if (r - x <= (u32)-bound)
        return x;
@@ -458,14 +461,14 @@ static inline u32 fastrand_max_u32_helper(struct fastrand *c, u32 bound)
       // Possible optimization for unlucky cases
       u32 y = r - x;
       do
-       r = fastrand_one(c);
+       r = fastrand_u32_helper(c);
       while (r >= y);
       return r % bound;
 #endif
     }
 
 #elif FASTRAND_MAX_U32_ALGORITHM == 2
-  u32 r = fastrand_one(c);
+  u32 r = fastrand_u32_helper(c);
   u64 m = (u64)r * bound;
   u32 l = (u32)m;
   if (l < bound)
@@ -477,7 +480,7 @@ static inline u32 fastrand_max_u32_helper(struct fastrand *c, u32 bound)
        {
          // Implies l < t % bound, so we don't need to check it before switching to completely different loop.
          do
-           r = fastrand_one(c);
+           r = fastrand_u32_helper(c);
          while (r >= bound);
          return r;
        }
@@ -485,7 +488,7 @@ static inline u32 fastrand_max_u32_helper(struct fastrand *c, u32 bound)
       t = t % bound;
       while (l < t)
        {
-         r = fastrand_one(c);
+         r = fastrand_u32_helper(c);
          m = (u64)r * bound;
          l = (u32)m;
        }
@@ -501,7 +504,7 @@ static inline u32 fastrand_max_u32_helper(struct fastrand *c, u32 bound)
   y |= y >> 8;
   y |= y >> 16;
   do
-    r = fastrand_one(c) & y;
+    r = fastrand_u32_helper(c) & y;
   while (r >= bound);
   return r;
 
@@ -584,7 +587,7 @@ void fastrand_mem(struct fastrand *c, void *ptr, size_t size)
     {
       if ((intptr_t)p & 3)
        {
-         uint x = fastrand_one(c);
+         u32 x = fastrand_u32_helper(c);
          while ((intptr_t)p & 3)
            {
              *p++ = x;
@@ -605,14 +608,14 @@ void fastrand_mem(struct fastrand *c, void *ptr, size_t size)
       for (size_t n = size >> 2; n--; )
 #endif
        {
-         *(u32 *)p = fastrand_one(c);
+         *(u32 *)p = fastrand_u32_helper(c);
          p += 4;
        }
       size &= 3;
     }
   if (size)
     {
-      uint x = fastrand_one(c);
+      u32 x = fastrand_u32_helper(c);
       while (size--)
        {
          *p++ = x;