Subject: lib/32258: rand() creates bad random numbers.
To: None <lib-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <lminder@gmx.net>
List: netbsd-bugs
Date: 12/06/2005 00:20:01
>Number:         32258
>Category:       lib
>Synopsis:       rand() creates bad random numbers.
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue Dec 06 00:20:01 +0000 2005
>Originator:     Lorenz Minder
>Release:        NetBSD 2.1_STABLE (and CVS mainline as of Dec 06, 2005)
>Organization:
	
>Environment:
System: NetBSD abra.kadabra 2.1_STABLE NetBSD 2.1_STABLE (MYKERNEL) #0: Tue Nov 29 14:33:07 CET 2005 lorenz@abra.kadabra:/usr/src/sys/arch/i386/compile/MYKERNEL i386
Architecture: i386
Machine: i386
>Description:

On NetBSD, rand() produces rather poor random numbers; the manual page
states that random() should be used instead of rand(). However, rand()
is a POSIX and ISO C standard function, whereas random() is not, and so
random() is not a substitute for rand().

Nothing in the C standard requires rand() to be a bad generator, and
generally, bad random number generators are a rather pointless (and
dangerous) thing to have.

The following patch makes rand() just be a wrapper to random() and
undeprecates it in the documentation.

There is the potential issue that rand() and random() share the same
state information, i.e., calling random() also causes rand()s state to
advance. This would of course be easy enough to fix, if needed, but no
code in existence seems to assume that random() and rand() are
uncoupled, and so it seems that fixing it would just add bloat without
addressing any real-world issue. In fact, in the Linux world, rand() has
been an alias to random() for a very long time now, so it is very
unlikely this change causes any problems at all.

>How-To-Repeat:

For example,

$ cat > x.c
#include <stdlib.h>
#include <stdio.h>

typedef unsigned long u_long;
static u_long next = 1;

int
my_rand()
{
        return (int)((next = next * 1103515245 + 12345)
          % ((u_long)RAND_MAX + 1));
}



int main(void)
{
        for(int i = 0; i < 10; ++i)
                printf("%d ", my_rand() & 1);
        putchar('\n');
        return 0;
}
^D
$ gcc -std=c99 x.c
$ ./a.out 
0 1 0 1 0 1 0 1 0 1 

>Fix:
Make rand() use random().

Index: stdlib/rand.3
===================================================================
RCS file: /cvsroot/src/lib/libc/stdlib/rand.3,v
retrieving revision 1.10
diff -u -r1.10 rand.3
--- stdlib/rand.3	7 Aug 2003 16:43:43 -0000	1.10
+++ stdlib/rand.3	5 Dec 2005 23:53:17 -0000
@@ -40,7 +40,7 @@
 .Nm rand ,
 .Nm srand ,
 .Nm rand_r
-.Nd bad random number generator
+.Nd random number generators
 .Sh LIBRARY
 .Lb libc
 .Sh SYNOPSIS
@@ -52,10 +52,6 @@
 .Ft int
 .Fn rand_r "unsigned int *seed"
 .Sh DESCRIPTION
-.Bf -symbolic
-These interfaces are obsoleted by
-.Xr random 3 .
-.Ef
 .Pp
 The
 .Fn rand
@@ -80,9 +76,37 @@
 .Pp
 The
 .Fn rand_r
-function is a reentrant interface to
-.Fn rand ;
-the seed has to be supplied and is maintained by the caller.
+function is a reentrant random number generator; the seed has to be
+supplied and is maintained by the caller.
+.Sh NOTES
+As of NetBSD 3.1,
+.Fn rand
+and
+.Fn srand
+are just wrappers for
+.Fn random
+and
+.Fn srandom
+respectively, and create numbers good enough for most non-cryptographic
+purposes.
+.Pp
+However,
+.Fn rand_r
+uses a traditional linear congruential generator. By design, it has only
+a single unsigned integer of state information, much less than what
+.Fn rand
+uses.  This is too little for many applications, and
+.Fn rand_r
+should only be used when the random numbers need not be very good.
+.Pp
+Traditional implementations of
+.Fn rand
+had very little randomness in the lower order bits.  To get acceptable
+random numbers on most systems, only the higher order bits should be
+used.  For example, to get random numbers from 0 to 15 (including), it
+is better to do (int)((rand() / (RAND_MAX + 1.0)) * 16) than rand() %
+16. On many systems, using the second formula will just cycle through
+the 16 possible values, repeating the same pattern over and over.
 .Sh SEE ALSO
 .Xr random 3
 .Sh STANDARDS
Index: stdlib/rand.c
===================================================================
RCS file: /cvsroot/src/lib/libc/stdlib/rand.c,v
retrieving revision 1.10
diff -u -r1.10 rand.c
--- stdlib/rand.c	7 Aug 2003 16:43:43 -0000	1.10
+++ stdlib/rand.c	5 Dec 2005 23:53:17 -0000
@@ -38,9 +38,17 @@
 #endif
 #endif /* LIBC_SCCS and not lint */
 
+#define USE_RANDOM_FOR_RAND
+
 #include <sys/types.h>
 #include <stdlib.h>
 
+#ifndef USE_RANDOM_FOR_RAND
+
+/* If USE_RANDOM_FOR_RAND is not defined, the old LC generators are
+ * used.
+ */
+
 static u_long next = 1;
 
 int
@@ -56,3 +64,33 @@
 {
 	next = seed;
 }
+
+#else
+
+/*
+ * This version of rand() uses the better random numbers provided by
+ * random(3) and srandom(3).
+ */
+
+#if RAND_MAX != ((1u << 31) - 1u)
+  /* The manual page of random() does not give any reference to
+   * RAND_MAX, but the limits on the range for rand() and random()
+   * coincide, as long as RAND_MAX == 2^31 - 1.
+   */
+#  error RAND_MAX is different from 2^31 - 1.
+#endif
+
+int
+rand()
+{
+	return random();
+}
+
+void
+srand(seed)
+	u_int seed;
+{
+	srandom((unsigned long)seed);
+}
+
+#endif
Index: stdlib/random.3
===================================================================
RCS file: /cvsroot/src/lib/libc/stdlib/random.3,v
retrieving revision 1.18
diff -u -r1.18 random.3
--- stdlib/random.3	7 Aug 2003 16:43:43 -0000	1.18
+++ stdlib/random.3	5 Dec 2005 23:53:17 -0000
@@ -70,13 +70,8 @@
 have (almost) the same calling sequence and initialization properties as
 .Xr rand 3
 and
-.Xr srand 3 .
-The difference is that
-.Xr rand 3
-produces a much less random sequence \(em in fact, the low dozen bits
-generated by
-.Xr rand 3
-go through a cyclic pattern.
+.Xr srand 3 ,
+and in fact, in the NetBSD C library, both can be used interchangeably.
 All the bits generated by
 .Fn random
 are usable.
@@ -173,6 +168,3 @@
 .Bx 4.2 .
 .Sh AUTHORS
 .An Earl T. Cohen
-.Sh BUGS
-About 2/3 the speed of
-.Xr rand 3 .