RAND_MAX宏:有符号还是无符号?

14
我已经查阅了1999年的C标准,它仅规定RAND_MAX应至少为32767,但未说明此宏是否应展开为有符号或无符号整数。单一UNIX规范(link 1link 2)和Linux man(link)没有提供任何明确的解释。

人们可能认为RAND_MAX应该是一个有符号整数,因为这就是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(link):RAND_MAX被定义为值0x7fff
  • Tiny C Compiler 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
  • 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]范围内的有符号整数。

如果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规范?


Plaguer和Brodie(1989)的书《标准C》关于ANSI / ISO标准说RAND_MAX<integer constant expression ≥ 32767>。 K&R的《C编程语言》(第2版1988年)仅在与int rand()函数相关时提到它,称后者返回范围在0RAND_MAX之间的值。听起来对我来说是有符号的。 - martineau
@martineau 对我来说听起来不明确。integer并不仅指intunsignedint一样是integer - Alexey Frunze
一个不带修饰的int等同于signedsigned int,因此将相同的规则与语言规范中“整数”一词的所有用法相关联是合理的。除此之外,RAND_MAX唯一的目的是用于描述int rand()函数返回值的限制,这进一步加强了这种假设。也就是说,为什么要将某个东西定义为只用于指定库函数返回值可能的范围,而该库函数返回的是一个signed类型的数量呢? - martineau
除了标准不禁止它(即允许它)之外,我不知道为什么。整个问题的重点是要找到一个当前标准(或其前身,包括K&R)中明确要求RAND_MAX必须是有符号(甚至是无符号)或明确允许为有符号或无符号的地方,而不是一个合理的假设(这一部分我们没有问题)。目前的措辞隐含地允许两者。这可能是出于历史(兼容性)原因,以免使现有的无符号实现失效。 - Alexey Frunze
2个回答

7

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

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

然后,很明显,必须预期函数返回的最大值的定义具有与函数相同的类型。在其他地方,宏被定义为扩展到带有某种类型的表达式,因此在此处也是可能的。

至于你的问题,我认为最容易实现可移植性的方法是首先将值缩放到[0,1)之间的双精度浮点数中,例如通过执行rand() / (RAND_MAX + 1.0),然后从那里派生出所有的计算。无论如何,如果您的平台没有更好的伪随机生成器可用,您应该仅在最后一步使用rand()。例如,在POSIX系统上,rand48系列是一个方便的替代方案,具有良好的描述属性。


1
我认为如果今天设计的话,它不会返回 unsigned。这样做只会导致不必要的并且通常有害的提升到 unsigned,正如 OP 所描述的那样。 - R.. GitHub STOP HELPING ICE
3
我认为会这样做,并且RAND_MAX将是该无符号返回类型的最大值。在这里没有理由浪费一个比特来表示符号,也不需要让随机“数字”很容易组成更宽的随机比特向量。 - Jens Gustedt

3
答案是:该代码做出了不必要的假设,因此需要进行修复。该代码应在使用时将RAND_MAX强制转换为(int)
虽然编译器将其RAND_MAX宏定义为有符号数会更有意义,但标准仔细避免要求这样做。这使得任何代码盲目地假定它是有符号数都存在可移植性问题。

由于迄今为止没有人找到关于主要问题“有符号还是无符号”的权威答案,而且已经过了足够长的时间,看起来这似乎是标准中的一个缺陷,我认为现在可以关闭这个问题了。因为其他答案表达了额外的假设,我认为这些假设是不合理的,所以我选择了你的答案。 - Alexey Frunze
我不同意这是一个“毫无根据的假设”。请看我在问题下面的评论。 - martineau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接