Use *rand48 functions for random numbers

Hrvoje Niksic <[email protected]> Wed, 29 Jun 2005 00:22:00 +0200
Newsgroups gmane.comp.web.wget.patches
Message-ID <[email protected]>
As the subject says.  Always using rand(), even for floats in the [0,
1) range, is just too lame.


2005-06-29  Hrvoje Niksic  <[email protected]>

	* utils.c (random_number): Use lrand48 if available.
	(random_float): Use drand48 if available.

Index: src/utils.c
===================================================================
--- src/utils.c	(revision 1894)
+++ src/utils.c	(working copy)
@@ -1571,68 +1571,74 @@
   return 0;
 #endif /* neither TIOCGWINSZ nor WINDOWS */
 }
+
+/* Whether the rnd system (either rand or [dl]rand48) has been
+   seeded.  */
+static int rnd_seeded;
 
 /* Return a random number between 0 and MAX-1, inclusive.
 
-   If MAX is greater than the value of RAND_MAX+1 on the system, the
-   returned value will be in the range [0, RAND_MAX].  This may be
-   fixed in a future release.
-
+   If the system does not support lrand48 and MAX is greater than the
+   value of RAND_MAX+1 on the system, the returned value will be in
+   the range [0, RAND_MAX].  This may be fixed in a future release.
    The random number generator is seeded automatically the first time
    it is called.
 
-   This uses rand() for portability.  It has been suggested that
-   random() offers better randomness, but this is not required for
-   Wget, so I chose to go for simplicity and use rand
-   unconditionally.
+   This uses lrand48 where available, rand elsewhere.  DO NOT use it
+   for cryptography.  It is only meant to be used in situations where
+   quality of the random numbers returned doesn't really matter.  */
 
-   DO NOT use this for cryptographic purposes.  It is only meant to be
-   used in situations where quality of the random numbers returned
-   doesn't really matter.  */
-
 int
 random_number (int max)
 {
-  static int seeded;
+#ifdef HAVE_DRAND48
+  if (!rnd_seeded)
+    {
+      srand48 ((long) time (NULL) ^ (long) getpid ());
+      rnd_seeded = 1;
+    }
+  return lrand48 () % max;
+#else  /* not HAVE_DRAND48 */
+
   double bounded;
   int rnd;
-
-  if (!seeded)
+  if (!rnd_seeded)
     {
-      srand (time (NULL));
-      seeded = 1;
+      srand ((unsigned) time (NULL) ^ (unsigned) getpid ());
+      rnd_seeded = 1;
     }
   rnd = rand ();
 
-  /* On systems that don't define RAND_MAX, assume it to be 2**15 - 1,
-     and enforce that assumption by masking other bits.  */
-#ifndef RAND_MAX
-# define RAND_MAX 32767
-  rnd &= RAND_MAX;
-#endif
+  /* Like rand() % max, but uses the high-order bits for better
+     randomness on architectures where rand() is implemented using a
+     simple congruential generator.  */
 
-  /* This is equivalent to rand() % max, but uses the high-order bits
-     for better randomness on architecture where rand() is implemented
-     using a simple congruential generator.  */
+  bounded = (double) max * rnd / (RAND_MAX + 1.0);
+  return (int) bounded;
 
-  bounded = (double)max * rnd / (RAND_MAX + 1.0);
-  return (int)bounded;
+#endif /* not HAVE_DRAND48 */
 }
 
 /* Return a random uniformly distributed floating point number in the
-   [0, 1) range.  The precision of returned numbers is 9 digits.
+   [0, 1) range.  Uses drand48 where available, and a really lame
+   kludge elsewhere.  */
 
-   Modify this to use drand48() where available!  */
-
 double
 random_float (void)
 {
-  /* We can't rely on any specific value of RAND_MAX, but it must
-     always be greater than 1000.  */
-  int rnd1 = random_number (1000);
-  int rnd2 = random_number (1000);
-  int rnd3 = random_number (1000);
-  return rnd1 / 1000.0 + rnd2 / 1000000.0 + rnd3 / 1000000000.0;
+#ifdef HAVE_DRAND48
+  if (!rnd_seeded)
+    {
+      srand48 ((long) time (NULL) ^ (long) getpid ());
+      rnd_seeded = 1;
+    }
+  return drand48 ();
+#else  /* not HAVE_DRAND48 */
+  return (  random_number (10000) / 10000.0
+	  + random_number (10000) / (10000.0 * 10000.0)
+	  + random_number (10000) / (10000.0 * 10000.0 * 10000.0)
+	  + random_number (10000) / (10000.0 * 10000.0 * 10000.0 * 10000.0));
+#endif /* not HAVE_DRAND48 */
 }
 
 /* Implementation of run_with_timeout, a generic timeout-forcing
Index: configure.in
===================================================================
--- configure.in	(revision 1868)
+++ configure.in	(working copy)
@@ -204,7 +204,7 @@
 AC_FUNC_ALLOCA
 AC_FUNC_MMAP
 AC_FUNC_FSEEKO
-AC_CHECK_FUNCS(strptime timegm snprintf vsnprintf)
+AC_CHECK_FUNCS(strptime timegm snprintf vsnprintf drand48)
 AC_CHECK_FUNCS(usleep ftello sigblock sigsetjmp symlink)
 
 dnl We expect to have these functions on Unix-like systems configure