例如:
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如何处理这个问题。
感谢。
答案 0 :(得分: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)。在使用更多位来存储值而不是用于表示数值的系统上,公式将不准确。 (不过,我个人并不是真的知道任何这样的系统。)
答案 1 :(得分:3)
这是C ++标准在4.7.2节(整体转换)中所说的内容:
如果目标类型是无符号的,则结果值最小 无符号整数与源整数一致(模2 n ,其中n 是用于表示无符号类型的位数)。 [注意:在 这是两个补码表示,这种转换是概念性的 位模式没有变化(如果没有截断)。 - 后注]
希望这能回答你的问题。
答案 2 :(得分:2)
你问过C和C ++。请记住,它们是两种不同的语言。在这种特殊情况下,它们对无符号类型的操作具有相同的规则,但它们的表达方式不同。
引用当前(2011)ISO C标准的latest draft,第6.2.5p9节:
涉及无符号操作数的计算永远不会溢出,因为 结果无法由结果无符号整数表示 type是以大于最大值的数量减少的模数 可以由结果类型表示的值。
一元“ - ”运算符的描述仅表示结果是“其(提升的)操作数的否定”;它假定读者已阅读6.2.5以找出无符号整数的“负”。
在任何一种语言中,结果为:
unsigned int numA = 66;
unsigned int numB = -numA;
将UINT_MAX - 66U + 1U
存储在numB
中。 (U
后缀并不是必需的,但我将它们包括在内以强调这是根据无符号值定义的。)
答案 3 :(得分:0)
我被typeof(-Unsigned)咬了是无符号的。 VS2012编译器将此处理解为有趣的不可思议行为:
unsigned x = 0xFFFFFFFE; int y = -x / 2;
什么是“y”?
我原本期望x / 2 = 0x7FFFFFFF,然后 - (x / 2)= 0x80000001,即-2 ** 31-1。 相反,编译器生成(-x)= 0x00000002,( - x)/ 2 = 0x00000001。
我想对于边界值,它都是Deathstar 9000.叹气。