-1可以用4位二进制数(2's complement) 1111来表示。
15也被表示为1111。
那么,当CPU从内存中获取数值时,它如何区分15和-1呢?
-1可以用4位二进制数(2's complement) 1111来表示。
15也被表示为1111。
那么,当CPU从内存中获取数值时,它如何区分15和-1呢?
当CPU把一个字节从一个位置移动到另一个位置时,它并不关心这个字节是否为-1或15。不存在“有符号移动”(对于相同大小的目标位置没有有符号移动 - 对于更大或更小的目标位置有有符号移动)。
CPU只在对字节进行算术运算时才关心其表示方式。根据您选择的操作码(或代表您选择的编译器),CPU知道是否执行有符号或无符号算术运算。
大部分之前的答案提到了不同的操作码。对于更复杂的操作,如乘法和除法,这可能是正确的,但对于简单的加减法,CPU 的工作方式并非如此。
CPU 在其标志寄存器中保存有关指令结果的数据。在 x86(我最熟悉的)上,这里最重要的两个标志是“溢出”和“进位”标志。
基本上,CPU 不关心数字是有符号还是无符号,它们都被视为相同。当数字超过它所能包含的最高无符号值时,设置进位标志。当它超过或低于无符号数字的范围时,设置溢出标志。如果您使用无符号数字,则检查进位标志并忽略溢出标志。如果您使用带符号数字,则检查溢出标志并忽略进位标志。
以下是一些示例:
无符号:
1111(15)+ 1111(15)= 1110(14)
现在要做的是检查进位标志,在这种情况下该标志包含一个给出最终结果的值。
1 1110(30)
有符号:
1111(-1)+ 1111(-1)= 1110(-2)
在这种情况下,忽略进位标志,溢出标志应设置为零。
无符号:
0111(7)+ 0111(7)= 1110(14)
当您检查进位标志时,它应该为零。
有符号:
0111(7)+ 0111(7)= 1110(-2)
在这种情况下,将设置溢出标志,表示加法存在错误。
因此,总之,数字仅基于您对其的解释而有符号或无符号。 CPU 为您提供了区分它们所必需的工具,但不会自动进行区分。
在编译器层面,区分是基于数据类型的。如果数据类型是int,在C语言中,那么将分配4个字节给该变量。所以,2的补码中的15是00000000 00000000 00000000 00000000 00001111
,而-1是11111111 11111111 11111111 11111111
。编译器然后将其转换为CPU的相应操作码。CPU执行这个操作码,在这个层面上,一切都是以1和0的形式存在的。
unsigned char i=255;
,它的二进制表示为 11111111
。如果使用有符号字符,则 11111111
表示 -127。CPU 无法从语言中获取类型信息。那么它如何知道哪个是哪个?(我并不是说这个问题实际上是可以回答的,因为它完全取决于实际的 CPU/指令集,只是说你的答案并没有回答这个问题。) - Mat
11111111b + 00000001b
,@amalantony:如果两个量都是无符号的(假设是8位寄存器),则会发生溢出。如果它们是有符号的,则不会溢出。如果编译器没有关于类型的信息(例如在操作码或某些标志中)则无法区分它们之间的差异。 - Mat