6

例如:

unsigned int numA = 66; // or anything really
unsigned int numB = -numA;
unsigned int numC = numA & numB

我知道按位补码运算符可用于获取二进制补码(与 +1 结合使用)。

我问的原因是因为我在一些国际象棋引擎的代码中偶然发现了这一点。国际象棋引擎做了很多“hacky”事情来获得绝对速度,尤其是在每秒被调用数百万次的移动生成函数中。(这无助于它是魔术位板移动生成的一个例子——它们中最优化的)。特别是这个国际象棋引擎代码只能在 gcc 编译下正常工作(我怀疑)。

不同的编译器如何处理这个?特别是,与 VS Studio 2012 Express 中的 C++ 编译器相比,gcc 是如何处理这个问题的。

谢谢。

4

4 回答 4

12

标准中的相关引用实际上是这样的:

(§5.3.1/8)一元 - 运算符的操作数应具有算术或无范围枚举类型,结果是其操作数的否定。对整数或枚举操作数执行整数提升。无符号量的负数是通过从 2 n中减去其值来计算的,其中 n 是提升的操作数中的位数。结果的类型是提升的操作数的类型。

(这是来自 C++11;在旧版本中它曾经是 5.3.1/7。)

因此-num将被评估为 2 CHAR_BIT*sizeof(num) - num (‡)。结果将与操作数的类型相同(在整数提升之后),即它也将是无符号的。

我刚刚用 GCC 进行了测试,它似乎完全按照标准描述的方式执行操作。我假设 Visual C++ 也是如此。否则这是一个错误。


(‡)此公式假定相关的位数对应于内存中变量的大小(以位为单位)。正如 Keith Thompson 在评论中指出的那样,如果有填充位(即并非所有位都参与数值的表示,根据 §3.9.1/1 是可能的),这不可能是真的。在使用比用于表示数值的位更多的位来存储值的系统上,公式将不准确。(不过,就我个人而言,我实际上并不知道有任何这样的系统。)

于 2012-12-28T06:12:19.820 回答
3

这是 C++ 标准在第 4.7.2 节(积分转换)下所说的:

如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2 n,其中 n 是用于表示无符号类型的位数)。[ 注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。——尾注]

希望这能回答你的问题。

于 2012-12-28T06:10:13.920 回答
2

您询问了 C 和 C++。请记住,它们是两种不同的语言。在这种特殊情况下,它们对无符号类型的操作具有相同的规则,但它们的表达方式不同。

引用当前(2011)ISO C 标准的最新草案),第 6.2.5p9 节:

涉及无符号操作数的计算永远不会溢出,因为无法由结果无符号整数类型表示的结果会以比结果类型可以表示的最大值大一的数字为模减少。

一元“-”运算符的描述只是说结果是“其(提升的)操作数的负数”;它假定读者已经阅读了 6.2.5 以弄清楚无符号整数的“负数”是什么。

在任何一种语言中,结果:

unsigned int numA = 66;
unsigned int numB = -numA;

是存放UINT_MAX - 66U + 1UnumB. (U后缀并不是真正必要的,但我将它们包括在内是为了强调这都是根据无符号值定义的。)

于 2012-12-30T05:39:03.637 回答
0

我被 typeof(-Unsigned) 是无符号的。VS2012 编译器将其带到了有趣的难以理解的行为水平:

无符号 x = 0xFFFFFFFE;int y = -x/2;

什么是“y”?

我本来期望 x/2 = 0x7FFFFFFF,然后 -(x/2) = 0x80000001,即 -2**31-1。相反,编译器生成 (-x) = 0x00000002, (-x)/2 = 0x00000001。

我猜对于边界值,这都是Deathstar 9000。叹息。

于 2014-09-28T21:25:13.220 回答