由于32位系统无法管理2 ^ 33的数字(由于明显的32位限制),如何管理80位浮点数?
它应该要求“ 80位” ...
由于32位系统无法管理2 ^ 33的数字(由于明显的32位限制),如何管理80位浮点数?
它应该要求“ 80位” ...
Answers:
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的位数并不紧密相关。
-fomit-frame-pointer
用来取回该寄存器。
32位,64位和128位均指处理器的字长,可以将其视为“基本数据类型”。通常,这是传输到系统RAM或从系统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上),但以上示例考虑了任意精度值。
现在,将其扩展到问题,很简单地看到如何添加比可用寄存器大的数字-我们只是将问题分解为寄存器大小的大块,然后从那里开始工作。尽管如@MatteoItalia所述,x87 FPU堆栈具有对80位量的本机支持,但是在缺少该支持的系统中(或完全没有浮点单元的处理器!),必须在软件中执行等效的计算/操作。
因此,对于一个80位数字,在将每个32位段相加之后,还将检查溢出到第81位,并选择将高阶位清零。对于某些x86和x86-64指令,将自动执行这些检查/清零操作,其中指定了源操作数和目标操作数的大小(尽管仅从1字节宽开始以2的幂来指定)。
当然,使用浮点数,因为尾数和有效数字以偏移形式打包在一起,所以不能简单地执行二进制加法。在x86处理器上的ALU中,有一个硬件电路可以对IEEE 32位和64位浮点数执行此操作。但是,即使没有浮点单元(FPU),也可以在软件中执行相同的计算(例如,通过使用GNU科学库,该库在体系结构上编译时使用FPU,而后退到软件算法如果没有可用的浮点硬件(例如,缺少FPU的嵌入式微控制器)。
如果有足够的内存,则还可以对任意数量(或“无限” –在实际范围内)精度进行计算,因为需要更高的精度才能使用更多的内存。GNU多精度库中存在此实现的一种实现,它允许对整数,有理数和浮点运算进行无限精度(当然,直到RAM已满)。
系统的内存架构可能只允许您一次移动32位-但这并不能阻止它使用更大的数字。
想想乘法。您可能知道乘法表最大为10x10,但是在一张纸上执行123x321可能没有问题:您将其分解为许多小问题,将单个数字相乘,并注意进位等。
处理器可以做同样的事情。在“旧时代”,您有8位处理器可以执行浮点数学运算。但是他们是slooooooow。
“ 32位”实际上是对处理器进行分类的一种方法,而不是一成不变的规则。通常,“ 32位”处理器具有32位通用寄存器。
但是,并没有明确要求处理器中的所有内容都必须以32位完成。例如,“ 32位”计算机具有28位地址总线并不是闻所未闻的,因为制造硬件便宜。出于相同的原因,64位计算机通常仅具有40位或48位内存总线。
浮点运算是大小变化的另一个地方。许多32位处理器支持64位浮点数。他们通过将浮点值存储在比通用寄存器宽的特殊寄存器中来实现。为了将这些大浮点数之一存储在特殊寄存器中,首先将数字分成两个通用寄存器,然后发出指令将它们组合为特殊寄存器中的浮点数。一旦进入这些浮点寄存器,就可以将这些值作为64位浮点运算,而不是作为一对32位半运算。
您提到的80位算术是这种情况的特例。如果您使用的是浮点数,那么您会熟悉浮点取整问题引起的不精确性。舍入的一种解决方案是提高精度,但随后您必须存储更大的数字,并迫使开发人员在内存中使用异常大的浮点值。
英特尔解决方案是浮点寄存器都是80位,但是将值移入/移出这些寄存器的指令主要是使用64位数字。只要您完全在Intel的x87浮点堆栈中操作,所有操作都将以80位精度完成。如果您的代码需要从浮点寄存器中提取这些值之一并将其存储在某个位置,则它将其截断为64位。
故事的寓意:当您深入研究事物时,诸如“ 32位”之类的分类总是比较麻烦!
“ 32位” CPU是其中大多数数据寄存器是32位寄存器,并且大多数指令对这些32位寄存器中的数据进行操作的CPU。一个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位浮点格式。