x87中的扩展(80位)双浮点,而不是SSE2 - 我们不会错过它?

时间:2010-07-08 16:57:49

标签: floating-point sse2 x87

我今天在读researchers discovering that NVidia's Phys-X libraries use x87 FP vs. SSE2。显然,对于速度超过精度的并行数据集来说,这将是次优的。但是,文章作者继续引用:

  

英特尔在2000年末开始推出P4时开始不鼓励使用x87.AMD自2003年K8以来已弃用x87,因为x86-64定义为SSE2支持;自2005年以来,VIA的C7一直支持SSE2。在64位版本的Windows中,x87不适用于用户模式,并且完全禁止在内核模式下使用。自2005年以来,业内所有人都推荐使用x87而不是x87,除非软件必须在嵌入式Pentium或486上运行,否则没有理由使用x87。

我想知道这件事。我知道x87内部使用80位扩展双精度值来计算值,而SSE2则不然。这对任何人都没关系吗?这对我来说似乎很惊讶。我知道当我对平面中的点,线和多边形进行计算时,在进行减法时,值可能出乎意料地错误,并且由于缺乏精度,区域可能会折叠并且线条会相互别名。我想,使用80位值与64位值可能会有所帮助。

这是不正确的?如果没有,如果逐步淘汰x87,我们可以使用什么来执行扩展的双FP操作?

4 个答案:

答案 0 :(得分:24)

x87的最大问题基本上是所有寄存器操作都是以80位完成的,而大多数时候人们只使用64位浮点数(即双精度浮点数)。发生的是,您将64位浮点数加载到x87堆栈中,并将其转换为80位。您以80位对其执行某些操作,然后将其存储回内存,将其转换为64位。你得到的结果与你用64位完成所有操作的结果不同,并且使用优化编译器,一个值可能会经历多少次转换是非常难以预测的,因此很难验证你是否得到了“在做回归测试时纠正“答案。

另一个问题,仅从编写汇编的人的观点(或者在为编译器编写代码生成器的情况下间接编写汇编)的观点来看,x87使用寄存器堆栈,而SSE使用单独访问的寄存器使用x87,你有一堆操作堆栈的额外指令,我想英特尔和AMD宁愿让他们的处理器使用SSE代码快速运行,而不是试图让那些额外的堆栈操作x87指令快速运行。

顺便说一句,如果您遇到不准确的问题,您可能需要查看文章“What every programmer should know about floating-point arithmetic”,然后可能会使用任意精度数学库(例如GMP)。

答案 1 :(得分:5)

为了正确使用扩展精度数学,语言必须支持可用于存储中间计算结果的类型,并且可以替换产生这些结果的表达式。因此,给出:

void print_dist_squared(double x1, double y1, double x2, double y2)
{
  printf("%12.6f", (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}

应该有一些类型可以用来捕获和替换公共子表达式x2-x1y2-y1,允许将代码重写为:

void print_dist_squared(double x1, double y1, double x2, double y2)
{
  some_type dx = x2-x1;
  some_type dy = y2-y1;
  printf("%12.6f", dx*dx + dy*dy);
}

不改变程序的语义。不幸的是,ANSI C未能在执行扩展精度计算的平台上指定可用于some_type的任何类型,并且指责英特尔存在扩展精度类型而不是责怪ANSI&#更为常见。 39;拙劣的支持。

事实上,扩展精度类型在没有浮点单元的平台上具有与在x87处理器上一样多的价值,因为在这样的处理器上,像x + y + z这样的计算需要执行以下步骤:

  1. 将尾数,指数和x的可能符号打包成单独的寄存器(指数和符号通常可以"双层")
  2. 同样打开包装。
  3. 使用较低的指数(如果有)右移该值的尾数,然后加上或减去这些值。
  4. 如果x和y有不同的符号,左移尾数直到最左边的位为1并适当调整指数。
  5. 将指数和尾数包装回双重格式。
  6. 解压缩该临时结果。
  7. 解包z。
  8. 使用较低的指数(如果有)右移该值的尾数,然后加上或减去这些值。
  9. 如果前面的结果和z有不同的符号,则左移尾数直到最左边的位为1并适当调整指数。
  10. 将指数和尾数包装回双重格式。
  11. 使用扩展精度类型将允许删除步骤4,5和6。由于53位尾数太大而不适合少于4个16位寄存器或2个32位寄存器,因此使用64位尾数执行加法并不比使用53位尾数慢,因此,使用扩展精度数学可以提供更快的计算,并且在支持适当类型的语言中没有缺点来保存临时结果。英特尔没有理由提供一个FPU,它能够以非emU芯片上最有效的方式执行浮点数学运算。

答案 2 :(得分:2)

另一个答案似乎表明使用80位精度是一个坏主意,但事实并非如此。它在保持不精确性方面起着有时至关重要的作用,例如, W. Kahan的着作。

如果您可以快速使用它,请始终使用80位中间算术。如果这意味着你必须使用x87数学,那么,这样做。对它的支持是无处不在的,只要人们继续做正确的事情,它就会无处不在。

答案 3 :(得分:0)

双精度比f80(约2.5个半字节/数字)小11位,对于许多应用程序(主要是游戏)来说它不会受到伤害。但是你需要所有准确的空间程序或医疗应用程序。

当有人说f80(并且不鼓励它)在堆栈上运行时,它有点误导。 FPU寄存器和操作类似于堆栈操作,也许这让人感到困惑。它实际上是基于内存的(加载/存储),而不是堆栈本身,例如,调用像cdecl stdcall这样的实现,它实际上通过堆栈传递参数。并没有错。

SSE的一大优势实际上是序列化操作,一次有2,4,8个值,有许多varian操作。是的,您可以直接转移到注册,但最后还是会将这些值传输到内存中。

f80的最大缺点是,它的奇数为10字节长,它会破坏对齐。您必须将它们对齐16以便更快地访问。但对阵列来说并不是真的可行。

你仍然必须使用fpu进行三角函数和其他trancedental数学运算。对于asm来说,有很多f80技巧非常有趣和有用。

对于游戏和常规的简单应用程序(几乎所有),你可以使用双倍而不会让某人死亡。但对于一些严肃的,数学或科学的应用程序,你只能放弃f80。