一元运算符“ - ”对C / C ++(以及不同的编译器)中的无符号数据类型做了什么?

时间:2012-12-28 06:06:01

标签: c++ c compiler-construction unsigned

例如:

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 个答案:

答案 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.叹气。