3

我最近一直在研究表示数字的补码系统,据我了解,数字 0 有两种变体。有一个负零 (-0) 和一个正零 (+0)。

我的问题是,在一个补码架构上,这个异常在 C 中究竟是如何处理的?C 是区分 -0 和 +0 还是将这两种形式都简单地视为零。

如果在测试为零时 +0 和 -0 都返回 TRUE,那么我想知道如果我们输入 -0 作为其输入,以下示例代码将如何计算整数中设置的位数。

int bitcount(int x)
{
    int b;
    for (b = 0; x != 0; b++)
        x &= (x-1);
    return b;
}

由于 -0 在一个补码中将其所有位设置为 1,因此 -0 应该返回任何其他数字中设置的最高位数;但是,该代码似乎无法通过 的循环测试条件x != 0,甚至不会进入循环,从而给出不正确的结果。

是否有可能在 C 中,在一个补码体系结构中,使循环条件对正零敏感,如下所示:x != +0另外,如果我从 +0 中减去 1,我会得到 -0 还是 -1。换句话说,+0 - 1 = -0 在一个补码体系结构中吗?

总而言之,为了在这个讨论中不要走得太远,我只是想知道 C 如何处理一个补码体系结构中数字 0 的特性。

4

2 回答 2

7

在一个补码架构上,“符号位和所有值位为 1”的值是“陷阱表示”还是正常值是实现定义的。如果它是一个陷阱表示,任何试图用它做任何事情,甚至首先创建它,都会引发未定义的行为。如果它是一个正常值,它就是一个“负零”,并且有一个明确的允许产生它的操作列表:

如果实现支持负零,它们只能通过以下方式生成:

  • &、|、^、~、<< 和 >> 运算符,其操作数产生这样的值;
  • +、-、*、/ 和 % 运算符,其中一个操作数为负零且结果为零;
  • 基于上述情况的复合赋值运算符。

未指定这些情况实际上是生成负零还是正常零,以及当存储在对象中时负零是否变为正常零。

(C11/N1570,第 6.2.6.2 节第 3 段。)

负零是否等于正常零似乎也未指定(通过省略)。类似的规则适用于符号和幅度架构。

因此,这归结为,您的示例代码的行为是实现定义的,而实现可能无法有效地定义它。您需要查阅这个假设的补码机器的编译器和架构手册,以确定它是否按照您的意愿执行。

然而,整个问题没有实际意义,因为至少 25 年没有人制造出非二进制补码 CPU。人们希望将来对 C 标准的修订将不再允许这种可能性。它会简化很多事情。

于 2017-07-23T00:08:02.667 回答
0

要回答您的问题,有两种可能性需要考虑:

  • 如果设置了所有位的位模式是陷阱表示(C 标准明确允许),则将此类值传递给函数具有未定义的行为。

  • 如果允许此位模式,则它是负零的补码表示,它应该比较等于0。在这种情况下,所编写的函数将具有定义的行为,并将返回0,因为初始循环测试为假。

如果函数以这种方式编写,结果会有所不同:

int bitcount32(int x) {
    // naive implementation assuming 31 value bits
    int count = 0, b;
    for (b = 0; b < 31; b++) {
        if (x & (1 << b))
           count++;
        }
    }
    return count;
}

在这个补充架构上,bitcount32(~0)将评估为31

(x & (1 << b))使用x具有特定位模式并在要定义b的范围内的参数,计算结果为,与二进制补码和符号/大小架构上的结果不同。1 << b1 << b

但是请注意,发布的实现对于INT_MINas的参数具有未定义的行为,x-1导致有符号算术溢出。强烈建议始终使用无符号类型进行按位和移位操作。

于 2017-09-25T13:32:18.600 回答