Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are drand48() and friends obsolete?

Why are drand48() and friends obsolete? After all, they seem superior to the standard libc rand(). Have I missed something?

The manual pages for rand() and drand48() also seem at odds. The first recommends the second, and the second states that it is obsolete and the first should be used. (Though, to be fair, a lot of people who understand the math behind PRNGs have issues with the man pages for these functions because they are poorly-worded and in some instances just wrong.)

Still, I can find no justification for the "obsolete" status.

like image 229
Dúthomhas Avatar asked Aug 12 '14 16:08

Dúthomhas


2 Answers

UPDTATE: drand48 and friends are not obsolescent, and the man page no longer says they are. Apparently the change was made in response to this answer (which is kinda cool).

A git repo containing the Linux man pages is here:

https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git

The relevant log entry is:

commit 3db3ecf0ff358ab86ead91d767b8ef502bffe26b
Author: Michael Kerrisk <[email protected]>
Date:   2014-09-13 20:08:10 -0700

    drand48.3: Remove crufty text about SVID 3 marking drand48() obsolete
    
    See http://bugs.debian.org/758293
    
    drand48() is in current POSIX. It's unclear why SVID 3 would
    have marked it obsolete, but that's crufty information that
    only serves to pointlessly worry people.
    
    Reported-by: Lorenzo Beretta <[email protected]>
    Signed-off-by: Michael Kerrisk <[email protected]>

See also this bug report: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=758293, which triggered the change.

My original answer follows.


The man page on my system (which is from the Linux man-pages project says:

These functions are declared obsolete by SVID 3, which states that rand(3) should be used instead.

SVID 3 was published in 1989.

SVID 4 (the link is to a 720-page PDF), published in 1995, documents drand48, erand48, lrand48, nrand48, mrand48, jrand48, srand48, seed48, and lcong48 and, like POSIX, says nothing about them being obsolete.

POSIX, as of 2013, says nothing about them being obsolete, obsolescent, or deprecated.

I haven't found a copy of SVID 3, so I don't know why it would have declared those functions obsolete, but apparently that decision was reconsidered later. The statement in the man page seems like out-of-date information. I wouldn't worry about it.

As for which function you should use, the C standard rand() function is the most portable (unless you recompile a different one from source code). Some rand() implementations are poor quality, with the low-order bits repeating in a very regular pattern; others are slightly better.

If you don't need high quality pseudo-random numbers, you might as well use rand() (seeded by calling srand() with a reasonable value, e.g., srand(time(NULL))).

If you do need high-quality pseudo-random numbers, it's likely that none of these functions is good enough, I'd advise against using any of them for cryptography, for example. You can use /dev/urandom or /dev/random if your system supports it. I've heard good things about the Mersenne Twister, but I lack the expertise to comment further.

(BTW, if you do a Google search for SVID, watch out for Svið).

like image 173
Keith Thompson Avatar answered Nov 15 '22 19:11

Keith Thompson


A note about standards:

  • rand() is part of the C standard, so it must be provided. Its implementation is unspecified, but in glibc, it uses the same algorithm as random(). From man 3 rand:

    The versions of rand() and srand() in the Linux C Library use the same random number generator as random(3) and srandom(3),...

  • drand48() is part of the POSIX standard. Its algorithm appears to be specified, it must use a 48-bit linear congruential generator with the following formula (according to man 3 drand48):

       Xn+1 = (aXn + c) mod m, where n >= 0
    
  • random() is part of the POSIX standard. It uses 31 words of state and an unspecified algorithm, according to man 3 random:

    The random() function uses a nonlinear additive feedback random number generator employing a default table of size 31 long integers to return successive pseudo-random numbers in the range from 0 to RAND_MAX. The period of this random number generator is very large, approximately 16 * ((2^31) - 1).

So, POSIX has these three random number generators. The best one is random() or rand(), which are the same in glibc (but probably not the same on other systems). The drand48() generator is a very simple type (linear congruential) with a relatively small amount of state (48 bits) so it should be avoided. None of the random number generators discussed here are necessarily suitable for e.g. Monte Carlo simulation, but drand48() is probably much worse than rand() or random().

Which one should I use?

I would always avoid drand48() because it is a tiny little linear congruential generator with a small state, and it is only available on POSIX systems. On POSIX systems, random() is usually available, and is better.

I would usually avoid rand() because on many systems it is a poor generator, often a linear congruential generator that is even smaller than drand48(), and its least significant bits on some systems are cyclical. If you don't need good random numbers, then rand() is fine.

I would use random() on any POSIX system if I needed random numbers but didn't care very deeply about how they are generated.

You can always use your own random number generator: if you want a good, portable random number generator, this is your only choice. Mersenne Twister has been a popular choice in the past, although smaller generators seem to be popular these days.

like image 24
Dietrich Epp Avatar answered Nov 15 '22 21:11

Dietrich Epp