例如:
unsigned int numA = 66; // or anything really
unsigned int numB = -numA;
unsigned int numC = numA & numB
我知道按位补码运算符可以用来得到这两个数的补码(与a +1结合使用)。
我问这个问题的原因是因为我在一些国际象棋引擎的代码中偶然发现了这个问题。象棋引擎做了很多"黑客"的事情来获得绝对的速度,特别是在每秒被调用数百万次的移动生成函数中。(这并没有帮助,这是一个神奇的位板移动生成的例子-其中最优化的)。特别是这个象棋引擎代码只能在gcc编译下正确工作(我怀疑)。
不同的编译器如何处理这个?特别是,与VS Studio 2012 Express中的c++编译器相比,gcc是如何处理这个问题的。
谢谢。
标准的相关引用实际上是这样的:
(§5.3.1/8)一元操作符的操作数必须是算术或无作用域枚举类型,其结果是其操作数的反数。整型提升是在整型或枚举操作数上执行的。无符号数的负数是通过2n减去它的值来计算的,其中n是提升操作数的位数。结果的类型为提升操作数的类型。
(这是来自c++ 11;旧版本为5.3.1/7)
因此-num
将被计算为2CHAR_BIT*sizeof(num) - num (‡)。结果将与操作数的类型相同(在整数提升之后),即它也将是无符号的。
我刚刚用GCC进行了测试,它似乎完全按照标准所描述的方式执行操作。我假设Visual c++也是如此;否则就是一个bug。
(‡)该公式假定的相关位数与变量在内存中的大小(以位为单位)相对应。正如Keith Thompson在评论中指出的那样,如果有填充位(即不是所有位都参与数值的表示,这是可能的,根据§3.9.1/1),这是不可能的。在一个存储值的比特数多于表示数值的比特数的系统中,公式将不准确。(我个人并不知道有这样的系统)
c++标准在第4.7.2节(整型转换)中是这样说的:
如果目标类型为无符号,则结果值最小无符号整数与源整数(模2n,其中n是用于表示unsigned类型的位数)。[注:在一个二的补表示,这种转换是概念性的和位模式没有改变(如果没有截断)。-end note]
希望这能回答你的问题
您同时问到C和c++。记住它们是两种不同的语言。在这个特殊的例子中,它们对unsigned类型的操作有相同的规则,但是它们的表达方式不同。
引用当前(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)是Unsigned咬了一下。VS2012编译器将此带入了一个有趣的不可思议的行为级别:
unsigned x = 0xFFFFFFFE;Int y = -x/2;
"y"是什么?
我希望x/2 = 0x7FFFFFFF,然后-(x/2) = 0x80000001,即-2**31-1。相反,编译器生成(-x) = 0x00000002, (-x)/2 = 0x00000001。
我猜对于边界值,它都是死亡之星9000。叹息。