tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: mrand48 broken on 64-bit systems



On Oct 21, 2013, at 5:44 PM, enh <enh%google.com@localhost> wrote:

> The mrand48() and jrand48() functions return signed long integers
> uniformly distributed over the interval [-2**31,2**31].
> http://pubs.opengroup.org/onlinepubs/7990989799/xsh/drand48.html
> 
> the freebsd, netbsd, and openbsd mrand48 implementations all just
> assume that long is 32-bit, and are thus broken on 64-bit
> architectures. (found running Android's bionic unit tests on x86_64.)
> 
> a cast appears to be all that's missing (though i don't know whether
> you support architectures where sizeof(int) != 4, in which case you'll
> want int32_t instead):
> 
> diff --git a/libc/upstream-netbsd/libc/stdlib/mrand48.c
> b/libc/upstream-netbsd/libc/stdlib/mrand48.c
> index c787ce6..b1083a1 100644
> --- a/libc/upstream-netbsd/libc/stdlib/mrand48.c
> +++ b/libc/upstream-netbsd/libc/stdlib/mrand48.c
> @@ -29,5 +29,5 @@ long
> mrand48(void)
> {
>  __dorand48(__rand48_seed);
> - return ((long) __rand48_seed[2] << 16) + (long) __rand48_seed[1];
> + return (int) ((long) __rand48_seed[2] << 16) + (long) __rand48_seed[1];
> }

It seems at first glance it would make more sense to replace the longs
by int32_t and avoid the 64-bit ops on LP64.  Alas, it's more subtle than
that.  For jrand48/mrand48 which return signed int32s, the easiest and
cleanest fix to change the return to:

        return (int16_t)__rand48_seed[2] * 65536 + __rand48_seed[1];

The multiple causes promotion to int and thus the int will be sign
extended to long if needed.  Let the compiler optimize the multiply to
a shift.  lrand/nand returns change to:

        return __rand48_seed[2] * 32768 + (__rand48_seed[1] >> 1);

And all casts go away.  the multiply promotes everything to unsigned int.





Home | Main Index | Thread Index | Old Index