谁定义了整数的符号反转模式?

时间:2016-12-14 22:06:51

标签: c++ twos-complement

二进制补码意味着简单地反转一个数字的所有位我得到了-i-1:

~0是-1

~01000001是10111110

~65是-66

等。要切换整数的符号,我必须使用实际的减号。

int i = 65; int j = -i; 
cout << j; // -65

在哪里定义了实际行为,并且其责任是确保二进制补码模式(使数字为负反转所有位并加1)?我甚至不知道这是硬件还是编译器操作。

3 个答案:

答案 0 :(得分:4)

通常由CPU硬件完成。

某些CPU具有计算数字负数的指令。在x86架构中,它是NEG指令。

如果不是,可以使用乘法运算符,将数字乘以-1来完成。但是,许多程序员利用您发现的身份,补充数字,然后添加1.参见

How to convert a positive number to negative in assembly

答案 1 :(得分:3)

原因很简单:与0和添加的一致性。

你想要添加对正数和负数的工作相同没有特殊情况......特别是,递增-1乘1必须得0。

经典溢出增量产生值0的唯一位序列是全1位序列。如果增加1,则全部为零。这就是你的-1:全1,即0的按位否定。 现在我们有(假设8位整数,每行递增1)

-2: 11111110 = ~1
-1: 11111111 = ~0
 0: 00000000 = ~-1
+1: 00000001 = ~-2

如果你不喜欢这种行为,你需要另外处理特殊情况,你将拥有+0和-0。最有可能的是,这样的CPU会慢得多。

如果您的问题是如何

int i = -j;
实现了

,这取决于您的编译器和CPU以及优化。通常它将与您指定的其他操作一起进行优化。但如果最终以

的形式执行,请不要感到惊讶
int i = 0 - j;

因为这可能需要1-2个cpu ticks来计算(例如,作为一个XOR或一个寄存器到自己得到0,然后一个SUB操作来计算0-j),它几乎不会成为瓶颈。加载j并将结果i存储在内存中的位置将要贵很多。实际上,有些CPU(MIPS?)甚至还有一个始终为零的内置寄存器。然后你不需要一个特殊的否定指令,你只需从$zero中减去j,通常是1个刻度。 据说当前的英特尔CPU识别这样的xor opersions并以0刻度执行它们,具有寄存器重命名优化(即它们让下一条指令使用一个零的新寄存器)。您在amd64上有neg,但快速xor rax,rax在其他情况下也很有用。

答案 2 :(得分:2)

C算术是根据值定义的。当代码是:

int i = 65; 
int j = -i; 

无论位代表如何,编译器都会发出所需的任何CPU指令,以j -65的值。

历史上,并非所有系统都使用了2的补充。 C编译器将根据CPU的功能选择一个负数系统,从而为目标CPU提供最有效的输出。

然而2的补码是一个非常常见的选择,因为它导致了最简单的算术算法。例如,相同的指令可用于带有符号和无符号整数的+ - *