I've looked up the C standard (from 1999) and it only says that RAND_MAX
should be at least 32767 but says nothing about whether this macro should expand to a signed or an unsigned int. The Single UNIX Specification (link 1, link 2) and Linux man (link) don't add any clarity.
One would think RAND_MAX
should be a signed int
since that's what rand()
returns.
However, I've found that some compilers define it as unsigned:
This makes seemingly innocuous code like the below become non-portable and blow up due to signed to unsigned promotion:
cos(w * t) + (rand() - RAND_MAX / 2) * 0.1 / (RAND_MAX / 2);
rand()
returns a signed int
in the range [0,RAND_MAX
].
If RAND_MAX
is defined as an unsigned int
, the value from rand()
gets promoted to unsigned int
too.
And if that's the case, the difference (rand() - RAND_MAX / 2)
becomes an unsigned difference of unsigned integers with the value in the ranges [0,RAND_MAX
-RAND_MAX
/2] & [UINT_MAX
+1-RAND_MAX
/2,UINT_MAX
-1] instead of being a signed difference of signed integers with the value in the range [-RAND_MAX
/2,RAND_MAX
-RAND_MAX
/2].
Anyhow, it seems like RAND_MAX
should be signed and most(?) compilers define it as such, but is there any authoritative source that says it should be signed? Older standard? K&R? Another UNIX spec?
Yes this looks like a defect in the standard.
First, nowadays probably nobody would define rand()
to return an int
. The intention is clearly to return a positive number and there is no error return that could use the negative returns. Such function would if it were introduced today be designed with an unsigned integer type as return type. My guess is that it predates C89 and the introduction of concept of unsigned integers to the language.
Then, clearly one has to expect that a definition of the maximum value that a function returns has the same type as the one of the function. At other places macros are well defined as expanding to expressions with a certain type so this would have been possible here, too.
As to your problem, I think the easiest to have something portable is to scale the value first to a double in [0, 1)
by doing something like rand()/(RAND_MAX+1.0)
and to derive all your computations from there. In any case you should use rand()
only as a last resort if your platform has no better pseudo-random generator available. E.g on POSIX systems the rand48
family is a convenient replacement with well described properties.
The answer is: that code is making an unwarranted assumption, so it needs to be fixed. What that code should have done is cast RAND_MAX
to (int)
on usage.
While it would make a bit more sense for compilers to define their RAND_MAX
macros as signed, the standard carefully avoids requiring them to do so. That makes it a portability bug for any code to blindly assume it to be signed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With