【问题标题】:RAND_MAX macro: signed or unsigned?RAND_MAX 宏:有符号还是无符号?
【发布时间】:2012-05-29 07:43:36
【问题描述】:

我查看了 C 标准(从 1999 年开始),它只说 RAND_MAX 至少应为 32767,但没有说明此宏应扩展为有符号整数还是无符号整数。 Single UNIX Specification (link 1, link 2) 和 Linux man (link) 并没有增加任何清晰度。

人们会认为RAND_MAX 应该是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(gcc 4.6.2 for Windows):#define RAND_MAX 0x7FFF
  • MS Visual Studio 2010 (link):RAND_MAX 定义为值 0x7fff
  • 微型 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
  • 数字火星 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_MAX-RAND_MAX/2] 和 [UINT_MAX+1-@987654341 范围内@/2,UINT_MAX-1] 而不是带符号整数的有符号差,其值在 [-RAND_MAX/2,RAND_MAX-RAND_MAX/2] 范围内。

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

【问题讨论】:

  • Plaguer & Brodie (1989) 编写的关于 ANSI/ISO 标准的Standard C一书说RAND_MAX<integer constant expression ≥ 32767>。 K&R 的 The C Programming Language(1988 年第 2 版)仅提及它与 int rand() 函数有关,称后者返回的值在 0RAND_MAX 范围内。听起来我签名了。
  • @martineau 对我来说听起来不详。 integer 不代表intunsignedinteger 一样int
  • 不合格的int 等价于signedsigned int,因此将相同的规则与语言规范中“整数”一词的所有用法相关联是合理的。除此之外,事实是RAND_MAX 的唯一目的是提供一些东西来描述int rand() 函数返回的值的限制,这只会进一步加强这一假设。即为什么 #defineunsigned 数量,它仅用于指定返回 signed 数量的库函数的可能返回值范围?
  • @martineau 除了标准不禁止(=允许)之外,我不知道为什么。问题的重点是试图找到一个不合理的假设(我们对此部分没有问题),而是在当前标准(或其前身,包括 K&R)中明确要求RAND_MAX 的位置被签名(甚至可能是未签名)或将被明确允许被签名或未签名。当前的措辞隐含地允许两者。这可能是出于历史(=兼容性)的原因,以免使现有的未签名实现无效。

标签: c


【解决方案1】:

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

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

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

至于您的问题,我认为最容易移植的方法是通过执行 rand()/(RAND_MAX+1.0) 之类的操作,首先将 [0, 1) 中的值缩放为两倍,然后从那里导出所有计算。在任何情况下,如果您的平台没有更好的伪随机生成器可用,您应该仅将rand() 用作最后的手段。例如,在 POSIX 系统上,rand48 系列是具有良好描述属性的便捷替代品。

【讨论】:

  • 如果它是今天设计的,我认为它不会返回unsigned。这样做只会导致对unsigned 的不必要且通常有害的提升,正如 OP 所描述的那样。
  • @R.. 反对意见:我认为会,RAND_MAX 将是该无符号返回类型的最大值。没有理由在这里浪费一点符号,也没有理由让随机“数字”轻松组成更广泛的随机位向量。
【解决方案2】:

答案是:该代码做出了毫无根据的假设,因此需要对其进行修复。该代码应该做的是在使用时将RAND_MAX 转换为(int)

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

【讨论】:

  • 由于到目前为止没有人找到主要问题“签名还是未签名”的权威答案。并且有足够的时间,它看起来确实是标准中的一个缺陷,我认为这个问题现在可以结束了。因为其他答案表达了额外的假设,我认为这不合理,所以我选择你的答案。
  • 我不同意这是一个“无根据的假设”。请参阅我在问题下的评论。
猜你喜欢
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多