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

例如:

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

我知道可以使用按位补码运算符来获得二进制补码(与+1一起使用)。

我问的原因是因为我在国际象棋引擎的一些代码中偶然发现了这个问题。 国际象棋引擎做了许多“hacky”事情以获得绝对速度,特别是在每秒数百万次的移动生成function中。 (它是魔术位板移动生成的一个例子 – 它们中最优化的所有)并没有帮助。 这个国际象棋引擎代码特别只能在gcc编译下正常工作(我怀疑)。

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

谢谢。

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

(§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)。 在使用更多位来存储值而不是用于表示数值的系统上,公式将不准确。 (不过,我个人并不是真的知道任何这样的系统。)

以下是C ++标准在4.7.2节(整体转换)中所说的内容:

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

希望这能回答你的问题。

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

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

涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模。

一元“ – ”运算符的描述仅表示结果是“其(提升的)操作数的否定”; 它假设读者已阅读6.2.5以确定无符号整数的“负”。

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

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

是将UINT_MAX - 66U + 1U存储在numB 。 ( U后缀不是必需的,但我包括它们以强调这是根据无符号值定义的。)

我被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.叹气。