我已经查阅了C标准(从1999年开始)并且它只表示RAND_MAX
应该至少为32767但是没有说明这个宏是否应该扩展为signed或unsigned int。单一UNIX规范(link 1,link 2)和Linux人(link)不会增加任何清晰度。
有人会认为RAND_MAX
应该是signed int
,因为这是rand()
返回的内容。
但是,我发现有些编译器将其定义为无符号:
这使得看似无害的代码如下所示变得不便携,并且由于签署了未签名的促销而爆炸:
cos(w * t) + (rand() - RAND_MAX / 2) * 0.1 / (RAND_MAX / 2);
rand()
返回[0,signed int
]范围内的RAND_MAX
。
如果将RAND_MAX
定义为unsigned int
,则rand()
的值也会提升为unsigned int
。
如果是这种情况,则差异(rand() - RAND_MAX / 2)
变为无符号整数的无符号差异,其范围为[0,RAND_MAX
- RAND_MAX
/ 2]& [UINT_MAX
+ 1 - RAND_MAX
/ 2,UINT_MAX
- 1]而不是有符号整数与[ - RAND_MAX
/ 2范围内的值的有符号差异, RAND_MAX
- RAND_MAX
/ 2]
无论如何,似乎应该签署RAND_MAX
并且大多数(?)编译器都是这样定义的,但有没有任何权威来源说它应该签名?较旧的标准? K&安培; R?另一个UNIX规范?
答案 0 :(得分:7)
是的,这看起来像标准中的缺陷。
首先,现在可能没有人会定义rand()
来返回int
。目的显然是返回正数,并且没有可以使用负回报的错误返回。如果今天引入的这个函数将被设计为使用无符号整数类型作为返回类型。我的猜测是它早于C89并且将无符号整数的概念引入该语言。
然后,显然人们必须期望函数返回的最大值的定义与函数的类型相同。在其他地方,宏被定义为扩展到具有特定类型的表达式,所以这也是可能的。
关于你的问题,我认为最简单的方法就是通过像[0, 1)
这样的方法将{1}}中的值首先缩放到双倍,然后从那里获得所有计算。在任何情况下,如果您的平台没有更好的伪随机生成器,您应该仅使用rand()/(RAND_MAX+1.0)
作为最后的手段。例如,在POSIX系统上,rand()
系列是一个方便的替代品,具有良好描述的属性。
答案 1 :(得分:3)
答案是:代码正在做出无根据的假设,因此需要修复。该代码应该做的是在使用时将RAND_MAX
投射到(int)
。
虽然编译器将其RAND_MAX
宏定义为已签名会更有意义,但标准小心地避免要求它们这样做。这使得任何代码盲目地认为它都要签名是一个可移植性的错误。