14

我查看了 C 标准(从 1999 年开始),它只说RAND_MAX应该至少为 32767,但没有说明这个宏是否应该扩展为有符号或无符号 int。单一 UNIX 规范(链接 1链接 2)和 Linux 手册(链接)并没有增加任何清晰度。

有人会认为RAND_MAX应该是 a signed int,因为这就是rand()回报。

但是,我发现一些编译器将其定义为无符号:

  • 古老的 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):#define 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#define RAND_MAX 0x7fff
  • 数字火星 C/C++ 8.52:#define RAND_MAX 32767

这使得像下面这样看似无害的代码变得不可移植并且由于已签名到未签名的促销而崩溃:

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

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

如果RAND_MAX定义为unsigned int,则 from 的值也会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 规范?

4

2 回答 2

7

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

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

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

至于你的问题,我认为最容易携带的东西是[0, 1)通过做类似的事情首先将值缩放到两倍,rand()/(RAND_MAX+1.0)然后从那里推导出你的所有计算。在任何情况下,rand()如果您的平台没有更好的伪随机生成器可用,您应该只作为最后的手段使用。例如,在 POSIX 系统上,该rand48族是具有良好描述的属性的方便替代品。

于 2012-05-22T19:35:39.097 回答
3

答案是:该代码在做出无根据的假设,因此需要对其进行修复。该代码应该执行的操作RAND_MAX取决于(int)使用情况。

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

于 2012-05-24T18:37:49.820 回答