让我们考虑一个8位处理器来简化我的问题。我知道-2被存储为其2的补码,即0b1111_1110,该数据块的小数表示为254,对吗?现在,我的问题是,由于ARM处理器的二进制表示形式相同,因此它们将如何区分“ -2”和“ 254”?
我试图查找整个互联网,每个人都在不断地解释处理器如何存储负数。我需要知道的是它们的区别。
答案 0 :(得分:7)
-2作为2的补码存储,即0b1111_1110,该数据块的小数表示为254,对吗?
是的,对于典型的现代系统来说确实如此。
现在,我的问题是ARM处理器将如何区分“ -2”和“ 254”,因为它们的二进制表示形式都相同?
处理器没有;编译器会这样做。
假设您有表达式value > 0
。变量value
和常量0
都有一个类型。根据这些类型,编译器选择要使用的CPU指令。因此,有符号和无符号比较可能导致不同的编译器输出。
处理器不知道代码中的类型。它只是执行这些选定的指令。
int icmp(int num) {
return num > 0;
}
int ucmp(unsigned int num) {
return num > 0;
}
icmp:
sub sp, sp, #16
str w0, [sp, 12]
ldr w0, [sp, 12]
cmp w0, 0
cset w0, gt
and w0, w0, 255
add sp, sp, 16
ret
ucmp:
sub sp, sp, #16
str w0, [sp, 12]
ldr w0, [sp, 12]
cmp w0, 0
cset w0, ne
and w0, w0, 255
add sp, sp, 16
ret
查看编译器如何生成略有不同的cset
指令。
答案 1 :(得分:4)
大多数处理器(包括Arm处理器)都无法区分带符号和无符号数字。可以将包含0b1111_1110的字节解释为具有值254的无符号整数或具有值-2的有符号整数。也可以将其解释为其他形式,例如浮点数,定点数,字符等。决定这种解释的是您对其执行的操作。
对于许多指令来说,值是有符号整数还是无符号整数都没有关系:有符号整数的表示旨在通过对字长进行模运算来使其轻量级。例如,将两个大小相同的值相加只是一条add
指令;值是否带符号都没有关系。
对于某些指令,处理器提供了不同的指令。例如,有两组instructions to copy a value to a larger register:SXTB(符号扩展字节)和朋友,以及UXTB(零扩展字节)和朋友。 UXT *指令将一个值复制到目标寄存器的低位,并将高位设置为零。 SXT *指令将一个值复制到目标寄存器的低位,并将高位设置为该值的高位,即,它们将该高位解释为符号位。
从C角度看,根据操作数使用正确的指令是编译器的工作。例如,如果编译器看到
uint8_t x = 0xfe;
uint32_t y = x + 3;
并确定编译的最佳方法是将x
存储在32位寄存器的低位中,并将y
存储为另一个32位寄存器,它将发出一个UXTB指令将y
的寄存器设置为0x000000fe
,然后是ADD
指令以获取所需的x
值。 (当然,在实践中,此代码段将被优化。)
答案 2 :(得分:2)
2的补码表示法具有很好的属性,只要没有溢出,加法和减法就不必关心数字是带符号的还是无符号的。 0b11111110+0b00000001
给出0b11111111
,在解释为有符号值时为-2 + 1 = -1
,在解释为无符号值时为254 + 1 = 255
。
当有符号性很重要时,有符号和无符号的机器代码指令会有所不同,例如,SMULL
和UMULL
的有符号和无符号乘法。比较值是通过使用不同的condition code suffixes来进行的,即在比较之后的指令中检查另一组标志是否为有符号和无符号类型,例如: BLE
用于签名的<=
,而BLS
用于未签名的<=
。
答案 3 :(得分:2)
CPU不知道存储在特定内存位置的数据的数据类型-数据类型仅在编程语言中存在。由编译器和/或程序员决定存储在特定位置的哪种类型。在进行C编程时,编译器通常会为您完成这项工作。
当我们说“ CPU是2的补码”时,我们指的是带符号算术指令的行为。也就是说,当您运行执行0 - 1
的CPU指令时,该指令将导致二进制数1111 .... 1111b,并在条件代码寄存器中设置了适当的标志,表示结果是否定的
程序员可以使用或选择忽略“负标志”,在这种情况下,机器代码已执行了明确定义的下溢。不幸的是,在C编程中不存在定义明确的有符号数的上溢/下溢的概念。因此,如果我们在C语言中产生上溢/下溢,则编译器可能会生成错误的代码。如果我们在汇编程序中执行相同的操作,那将永远不会发生,因为行为是在CPU级别上明确定义的。
答案 4 :(得分:-1)
这取决于变量的声明方式。
对于8位变量,分别具有带符号或无符号的标准类型uint8_t
和sint8_t
。
例如
#include <stdint.h>
uint8_t a = 254;
sint8_t b = -2;
如果要对此进行试验,还应该研究Integer promotion rules