RAND_MAX宏:签名还是未签名?

我查阅了C标准(从1999年开始),它只说RAND_MAX至少应该是32767,但是没有说明这个宏是否应该扩展为signed或unsigned int。 Single UNIX Specification( 链接1 , 链接2 )和Linux man( 链接 )不会增加任何清晰度。

有人会认为RAND_MAX应该是一个有signed int因为这就是rand()返回的内容。

但是,我发现有些编译器将其定义为unsigned:

  • 古老的Turbo C ++ 1.01:#define RAND_MAX 0x7FFFU
  • 不那么古老的C ++ Builder 5.5:#define RAND_MAX 0x7FFFU
  • 仍然活着的Open Watcom C / C ++ 1.9:#define RAND_MAX 32767U
  • DJGPP(用于DOS的gcc 3.3.4):#undefine RAND_MAX 2147483647
  • MinGW(适用于Windows的gcc 4.6.2):#define RAND_MAX 0x7FFF
  • MS Visual Studio 2010( 链接 ): RAND_MAX定义为值0x7fff
  • Tiny C编译器0.9.25:#define RAND_MAX 0x7FFF
  • lcc-win32 3.8:#define RAND_MAX 0x7fff
  • Pelles C 6.50:#define RAND_MAX 0x3fffffff OR #define RAND_MAX 0x7fff
  • Digital Mars C / C ++ 8.52:#define RAND_MAX 32767

这使得看似无害的代码如下所示变得不可移植,并且由于签署了未签名的促销而爆炸:

 cos(w * t) + (rand() - RAND_MAX / 2) * 0.1 / (RAND_MAX / 2); 

rand()返回[0, RAND_MAX ]范围内的signed int

如果将RAND_MAX定义为unsigned int ,则rand()的值也会被提升为unsigned int

如果是这种情况,则差值(rand() - RAND_MAX / 2)变为无符号整数的无符号差值,其范围为[0, RAND_MAXRAND_MAX / 2]和[ UINT_MAX + 1- RAND_MAX / 2, UINT_MAX -1]而不是有符号整数的有符号差值,其值在[ – RAND_MAX / 2, RAND_MAXRAND_MAX / 2]范围内。

无论如何,似乎RAND_MAX应该被签名并且大多数(?)编译器都是这样定义的,但是有没有任何权威来源说应该签名? 较旧的标准? K&R? 另一个UNIX规范?

答案是:代码正在做出无根据的假设,因此需要修复。 该代码应该做的是在使用时将RAND_MAX(int)

虽然编译器将RAND_MAX宏定义为有符号会更有意义,但标准小心地避免要求它们这样做。 这使得它成为任何代码盲目地认为它被签名的可移植性错误。

是的,这看起来像是标准中的缺陷。

首先,现在可能没有人会定义rand()来返回int 。 意图显然是返回正数,并且没有可以使用负回报的错误返回。 如果今天引入的这个函数将被设计为使用无符号整数类型作为返回类型。 我的猜测是它早于C89并将无符号整数的概念引入语言。

然后,显然人们必须期望函数返回的最大值的定义与函数的类型相同。 在其他地方,宏被定义为扩展到具有某种类型的表达式,所以这也是可能的。

至于你的问题,我认为最容易携带的东西是通过执行类似rand()/(RAND_MAX+1.0) ,首先将值扩展为[0, 1)的double,并从那里派生所有计算。 在任何情况下,如果你的平台没有更好的伪随机生成器,你应该只使用rand()作为最后的手段。 例如,在POSIX系统上, rand48系列是一个方便的替代品,具有良好的描述属性。