负数在内存中存储为 2 的补码,CPU 如何知道它是负数还是正数?

Posted

技术标签:

【中文标题】负数在内存中存储为 2 的补码,CPU 如何知道它是负数还是正数?【英文标题】:Negative numbers are stored as 2's complement in memory, how does the CPU know if it's negative or positive? 【发布时间】:2011-12-02 14:53:50 【问题描述】:

-1 可以用 4 位二进制表示为(2 的补码)1111

15 也表示为 1111。

那么,当 CPU 从内存中获取值时,它是如何区分 15 和 -1 的呢?

【问题讨论】:

【参考方案1】:

在编译器级别,区分基于数据类型。如果数据类型是 int,则为该变量分配 4 个字节(在 C 中)。所以 2 的补码中的 15 是 00000000 00000000 00000000 00000000 00001111 而 -1 是 11111111 11111111 11111111 11111111 。然后编译器将其转换为 CPU 的相应操作码。 CPU 执行这个操作码,在这个级别上,一切都是 1 和 0 的形式。

【讨论】:

这并不能回答问题,即 CPU 如何发挥作用。 CPU 不知道 C。 编译器为每个数字分配预定义的字节数(不管是什么语言,C 只是一个例子)。如果二进制版本由全 1 组成,则为 -1,如果在 0 位之前至少有 1 个,则将其解释为非 0 整数值。收到了吗? 这提出了一个问题,CPU 将如何区分 32 位值 -1 和 2^32 - 1,这两者在二进制补码中具有相同的位表示? 没有。问题是:假设我有unsigned char i=255; 那是二进制的1111111111111111 将是带符号字符的 -127。 CPU 无法访问该语言的类型信息。它怎么知道哪个是哪个? (我并不是说这个问题实际上是可以回答的,因为它完全取决于实际的 CPU/指令集,只是说你的答案不能回答问题。) @amal 当它是 4 位机器时会发生什么?【参考方案2】:

当一个字节从一个地方移动到另一个地方时,CPU 并不关心它是保持 -1 还是 15。没有“签名移动”之类的东西(到相同大小的位置 - 有更大或更小的目的地的签名移动)。

CPU 只在对字节进行算术运算时才关心表示。 CPU 根据您(或代表您的编译器)选择的操作码知道是执行有符号还是无符号运算。

【讨论】:

有符号和无符号算术仅在 1 补码的情况下才有意义。在 2 的补码中,符号是不言而喻的。 11111111b + 00000001b, @amalantony:如果两个数量都是无符号的(假设 8 位 regs),则会溢出。如果它们被签名,它不会溢出。如果编译器没有关于类型 somewhere 的信息(例如在操作码或某些标志中),则编译器无法区分 @Peter 系统如何判断 15(1111b)-1 =14 和 -1(1111b)-1(0001b)=-2 @amal:有符号与无符号确实在二进制补码表示方面有所不同。例如,指令集可能会为有符号和无符号比较提供单独的操作码。 C 编译器会根据操作数的 C 类型(有符号与无符号)发出适当的比较指令。 @MCG:那将是一个不好的例子,因为 1111b - 0001b = 1110b,可以正确解释为无符号 (15 - 1 = 14) 或有符号 (-1 - 1 = -2)。但是,乘法和除法需要有符号和无符号的不同指令。【参考方案3】:

CPU 不知道一个数字是有符号还是无符号。当编译器创建机器语言文件时,它会选择要执行的正确操作来对该数字进行数学运算。例如,如果您将变量声明为有符号类型,那么在机器语言中执行的操作将把该内存位置视为有符号值。

在任何类型的任何软件中,总是在您解释数据时赋予其意义。内存中的字节可以是有符号或无符号的数字、字符、音乐文件的一部分或图片中的像素等。赋予它意义的是你如何使用该字节。

【讨论】:

【参考方案4】:

之前的大多数答案都提到了单独的操作码。对于像乘法和除法这样更复杂的运算可能是这样,但对于简单的加法和减法运算,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 为您提供了区分它们的必要工具,但它本身并不能区分。

【讨论】:

【参考方案5】:

最小的可访问单元是 1 个字节。那是8位。在 8 位表示中,15 存储为 00001111。编译器根据符号位区分正数和负数。 MSB 是符号位。如果为 0 表示正数。如果是 1 表示负数。 15的二进制表示的MSB为0,表示正数,00001111对应+15。 -1 的 8 位二进制是 11111111,因为它的 MSB 为 1,它被取为负数。编译器首先取其 2 的补码,然后显示带有负号的数字。请记住,如果有 8 位来存储数字,那么您可以在其中存储的最大值是 (2^7)-1,它的表示形式需要 7 位。这意味着对于正数,MSB 始终为零。正如您的问题一样,如果我们假设使用 4 位来存储数字,那么 3 位可用于存储该值,因为最后一位是保留符号的保留位。对于 3 位,可以存储的最大值为 (2^3)-1=7。这意味着 15 不能存储在 4 位中。因此 1111 总是被编译器视为 -1。

点击下面的链接可以访问有如此棘手问题的 YouTube 频道。 www.YouTube。 com/watch?v=ZxRHOT3pzx4

【讨论】:

【参考方案6】:

在表示 15 的 2 的补码中,我们需要 5 位,2'complemnt 的范围是 -16 到 15,所以值变成 01111 这里 MSB 位是 0 所以它的正值 -1 会变成 11111

【讨论】:

以上是关于负数在内存中存储为 2 的补码,CPU 如何知道它是负数还是正数?的主要内容,如果未能解决你的问题,请参考以下文章

IT十八掌作业_java基础第二天_进制转换原理和补码存储方式

负数在计算机中如何存储

计算机如何知道二进制补码是负数?

Noip初赛整理

原码反码补码,负数表示法

JVM学习--数字存储,内存模型,指令重排