![32 位系统如何使用 80 位浮点数?](https://linux22.com/image/1417484/32%20%E4%BD%8D%E7%B3%BB%E7%BB%9F%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%2080%20%E4%BD%8D%E6%B5%AE%E7%82%B9%E6%95%B0%EF%BC%9F.png)
答案1
32 位 CPU 的含义之一是其寄存器为 32 位宽。这并不意味着它不能处理 64 位数字,只是它必须先处理低 32 位,然后再处理高 32 位。(这就是为什么 CPU 有进位标志)这比 CPU 将值加载到更宽的 64 位寄存器中要慢,但仍然是可能的。
因此,系统的“位数”不一定限制程序可以处理的数字大小,因为您始终可以将无法放入 CPU 寄存器的操作分解为多个操作。因此,它会使操作速度变慢、占用更多内存(如果您必须使用内存作为“暂存器”)并且编程更加困难,但操作仍然是可能的。
但是,对于 Intel 32 位处理器和浮点来说,这些都不重要,因为 CPU 的浮点部分有自己的寄存器,并且宽度为 80 位。(在 x86 早期的历史中,浮点功能是一个单独的芯片,从 80486DX 开始它就集成在 CPU 中。)
@Breakthrough 的回答启发了我添加这一点。
浮点值就其存储在 FPU 寄存器中而言,其工作方式与二进制整数值非常不同。
浮点值的 80 位分为尾数和指数(浮点数中还有“基数”,始终为 2)。尾数包含有效数字,指数决定这些有效数字的大小。因此,不会“溢出”到另一个寄存器,如果您的数字太大而无法容纳尾数,则指数会增加,并且会失去精度 - 即,当您将其转换为整数时,右边的小数位会丢失 - 这就是它被称为浮点的原因。
如果指数太大,就会出现浮点溢出,但由于指数和尾数绑定在一起,因此您无法轻易将其扩展到另一个寄存器。
我可能有些说法不准确或错误,但我相信这就是要点。(这维基百科文章更简洁地说明了上述内容。)
完全不同地工作也没关系,因为 CPU 的整个“浮点”部分都处于自己的世界中 - 您使用特殊的 CPU 指令来访问它等等。此外,对于问题的关键,由于它们是分开的,FPU 的位数与本机 CPU 的位数并不紧密相关。
答案2
32 位、64 位和 128 位均指字长处理器的,可以被认为是“基本数据类型”。通常,这是传输到/从系统 RAM 的位数,以及指针的宽度(尽管没有什么可以阻止您使用软件访问比单个指针可以访问的更多的 RAM)。
假设时钟速度恒定(以及架构中的其他所有东西都恒定),并且假设内存读/写速度相同(我们在这里假设 1 个时钟周期,但这与现实情况相差甚远),您可以在 64 位机器上的一个时钟周期内添加两个 64 位数字(如果算上从 RAM 中获取数字,则为三个):
ADDA [NUM1], [NUM2]
STAA [RESULT]
我们还可以做在 32 位机器上进行相同的计算...但是,在 32 位机器上,我们需要在软件中执行此操作,因为必须先添加低 32 位,补偿溢出,然后添加高 64 位:
ADDA [NUM1_LOWER], [NUM2_LOWER]
STAA [RESULT_LOWER]
CLRA ; I'm assuming the condition flags are not modified by this.
BRNO CMPS ; Branch to CMPS if there was no overflow.
ADDA #1 ; If there was overflow, compensate the value of A.
CMPS ADDA [NUM1_UPPER], [NUM2_UPPER]
STAA [RESULT_UPPER]
通过我编造的汇编语法,您可以轻松看到在字长较低的机器上,更高精度的运算需要的时间会呈指数级增长。这是 64 位和 128 位处理器的真正关键:它们允许我们在一次操作中处理更多的位数。有些机器包含使用进位添加其他数量的指令(例如ADC
在 x86 上),但上述示例考虑的是任意精度值。
现在,为了将其扩展到问题,很容易看到我们如何添加大于可用寄存器的数字 - 我们只需将问题分解为寄存器大小的块,然后从那里开始。尽管正如所提到的@MatteoItaliax87 FPU 堆栈对 80 位数量具有本机支持,在缺少此支持的系统中(或处理器完全缺少浮点单元!),必须执行等效的计算/操作在软件中。
因此,对于 80 位数,在添加每个 32 位段后,还会检查第 81 位是否溢出,并可选择将高位清零。这些检查/清零是针对某些 x86 和 x86-64 指令自动执行的,其中指定了源和目标操作数的大小(尽管这些大小仅以 2 的幂指定,从 1 字节宽开始)。
当然,对于浮点数,不能简单地执行二进制加法,因为尾数和有效数字以偏移形式打包在一起。在 x86 处理器的 ALU 中,有一个硬件电路可以对 IEEE 32 位和 64 位浮点数执行此操作;然而,即使没有浮点单元(FPU),也可以在软件中执行相同的计算(例如通过使用GNU 科学库,它在具有的架构上编译时使用 FPU,如果没有可用的浮点硬件,则会回退到软件算法(例如,对于缺少 FPU 的嵌入式微控制器)。
如果有足够的内存,还可以对随意的(或“无限” - 在现实范围内)精度,需要的精度越高,使用的内存就越多。这种实现存在于GNU 多精度库,允许对整数、有理数和浮点运算使用无限精度(当然,直到你的 RAM 已满)。
答案3
系统的内存架构可能只允许您一次移动 32 位 - 但这并不妨碍它使用更大的数字。
想想乘法。您可能知道 10x10 以内的乘法表,但您可能在纸上轻松完成 123x321 的运算:您只需将其分解为许多小问题,将各个数字相乘,并处理进位等。
处理器也能做同样的事情。在“过去”,你有 8 位处理器可以进行浮点运算。但它们的速度很慢。
答案4
“32 位”CPU 是指大多数数据寄存器都是 32 位寄存器,并且大多数指令都针对这些 32 位寄存器中的数据进行操作。32 位 CPU 也可能一次将数据传入和传出 32 位内存。大多数寄存器是 32 位并不意味着所有寄存器都是 32 位。简而言之,32 位 CPU 可以具有一些使用其他位数的功能,例如 80 位浮点寄存器和相应的指令。
正如 @spudone 在对 @ultrasawblade 的回答的评论中所说,第一个集成浮点运算的 x86 CPU 是 Intel i486(特别是 80486DX,而不是 80486SX),根据i486 微处理器程序员参考手册第 15-1 页,其数字寄存器中包含“八个可单独寻址的 80 位数字寄存器”。i486 具有 32 位内存总线,因此传输 80 位值需要 3 次内存操作。
486 一代的前身 i386 没有任何集成的浮点运算。相反,它支持使用外部浮点“协处理器”80387。这个协处理器的功能与 i486 中集成的功能几乎相同,您可以从中看到80387 程序员参考手册第 2-1 页。
80 位浮点格式似乎起源于 8087,它是 8086 和 8088 的数学协处理器。8086 和 8088 是 16 位 CPU(带有 16 位和 8 位内存总线),并且仍然能够使用 80 位浮点格式,利用协处理器中的 80 位寄存器。