C 如何处理补码架构中的数字 0?
How does C treat the number 0 in a one’s complement architecture?
我最近在研究表示数字的补码系统,据我了解,数字 0 有两种变体。负零 (-0) 和正零 (+0)。
我的问题是,在一个补码架构上,C 中究竟如何处理这种异常? C 是否区分 -0 和 +0,或者这两种形式都被简单地视为零。
如果在测试为零时 +0 和 -0 return 均为 TRUE,那么我想知道以下示例代码将如何计算整数中的设置位数,如果我们输入 -0 作为输入。
int bitcount(int x)
{
int b;
for (b = 0; x != 0; b++)
x &= (x-1);
return b;
}
因为 -0 在补码中的所有位都设置为 1,因此 -0 应该 return 设置为任何其他数字中的最高位;然而,这段代码似乎无法通过 x != 0
的循环测试条件,甚至不会进入循环,从而给出不正确的结果。
是否有可能在 C 语言中,在一个补码架构中,使循环条件对正零敏感,如下所示:x != +0
另外,如果我从 +0 中减去 1,我会得到-0 或 -1。换句话说,+0 - 1 = -0 在补码架构中吗?
总而言之,在这个讨论中不要离题太远,我只是想知道 C 如何处理补码体系结构中数字 0 的特殊性。
在一个补码架构上,值 "with sign bit and all value bits 1" 是 "trap representation" 还是正常值是实现定义的。如果它是一个陷阱表示,任何尝试用它做 任何事情,甚至首先创建它,都会引发未定义的行为。如果它是一个正常值,它是一个"negative zero",并且有一个明确的允许产生它的操作列表:
If the implementation supports negative zeros, they shall be generated only by:
- the &, |, ^, ~, <<, and >> operators with operands that produce such a value;
- the +, -, *, /, and % operators where one operand is a negative zero and the result is zero;
- compound assignment operators based on the above cases.
It is unspecified whether these cases actually generate a negative zero or a normal zero, and whether a negative zero becomes a normal zero when stored in an object.
(C11/N1570,第 6.2.6.2 节第 3 段。)
负零是否等于正常零似乎也未指定(通过省略)。类似的规则适用于符号和大小架构。
所以,归根结底,您的示例代码的行为是实现定义的,实现定义它可能没有帮助。您需要查阅此假设的补码机的编译器和体系结构手册,以确定它是否按照您的要求执行。
但是,整个问题都没有实际意义,因为至少 25 年来没有人制造过非二进制补码 CPU。人们希望 C 标准的未来修订将不再允许这种可能性;它会简化很多事情。
要回答您的问题,有 2 种可能性可供考虑:
如果设置了所有位的位模式是陷阱表示(这是 C 标准明确允许的),则将这样的值传递给函数具有未定义的行为。
如果允许这个位模式,它是负零的补码表示,应该比较等于0
。在这种情况下,编写的函数将具有定义的行为,并且将 return 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 << b
范围内,计算结果为 1 << b
,a与二进制补码和 sign/magnitude 架构的结果不同的值。
但是请注意,发布的实现对于 INT_MIN
的参数具有未定义的行为,因为 x-1
会导致有符号算术溢出。强烈建议始终使用无符号类型进行按位和移位操作。
我最近在研究表示数字的补码系统,据我了解,数字 0 有两种变体。负零 (-0) 和正零 (+0)。
我的问题是,在一个补码架构上,C 中究竟如何处理这种异常? C 是否区分 -0 和 +0,或者这两种形式都被简单地视为零。
如果在测试为零时 +0 和 -0 return 均为 TRUE,那么我想知道以下示例代码将如何计算整数中的设置位数,如果我们输入 -0 作为输入。
int bitcount(int x)
{
int b;
for (b = 0; x != 0; b++)
x &= (x-1);
return b;
}
因为 -0 在补码中的所有位都设置为 1,因此 -0 应该 return 设置为任何其他数字中的最高位;然而,这段代码似乎无法通过 x != 0
的循环测试条件,甚至不会进入循环,从而给出不正确的结果。
是否有可能在 C 语言中,在一个补码架构中,使循环条件对正零敏感,如下所示:x != +0
另外,如果我从 +0 中减去 1,我会得到-0 或 -1。换句话说,+0 - 1 = -0 在补码架构中吗?
总而言之,在这个讨论中不要离题太远,我只是想知道 C 如何处理补码体系结构中数字 0 的特殊性。
在一个补码架构上,值 "with sign bit and all value bits 1" 是 "trap representation" 还是正常值是实现定义的。如果它是一个陷阱表示,任何尝试用它做 任何事情,甚至首先创建它,都会引发未定义的行为。如果它是一个正常值,它是一个"negative zero",并且有一个明确的允许产生它的操作列表:
If the implementation supports negative zeros, they shall be generated only by:
- the &, |, ^, ~, <<, and >> operators with operands that produce such a value;
- the +, -, *, /, and % operators where one operand is a negative zero and the result is zero;
- compound assignment operators based on the above cases.
It is unspecified whether these cases actually generate a negative zero or a normal zero, and whether a negative zero becomes a normal zero when stored in an object.
(C11/N1570,第 6.2.6.2 节第 3 段。)
负零是否等于正常零似乎也未指定(通过省略)。类似的规则适用于符号和大小架构。
所以,归根结底,您的示例代码的行为是实现定义的,实现定义它可能没有帮助。您需要查阅此假设的补码机的编译器和体系结构手册,以确定它是否按照您的要求执行。
但是,整个问题都没有实际意义,因为至少 25 年来没有人制造过非二进制补码 CPU。人们希望 C 标准的未来修订将不再允许这种可能性;它会简化很多事情。
要回答您的问题,有 2 种可能性可供考虑:
如果设置了所有位的位模式是陷阱表示(这是 C 标准明确允许的),则将这样的值传递给函数具有未定义的行为。
如果允许这个位模式,它是负零的补码表示,应该比较等于
0
。在这种情况下,编写的函数将具有定义的行为,并且将 return0
因为初始循环测试为假。
如果函数这样写,结果会不一样:
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 << b
范围内,计算结果为 1 << b
,a与二进制补码和 sign/magnitude 架构的结果不同的值。
但是请注意,发布的实现对于 INT_MIN
的参数具有未定义的行为,因为 x-1
会导致有符号算术溢出。强烈建议始终使用无符号类型进行按位和移位操作。