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

cod*_*zen 35 floating-point sse2 x87

我今天正在阅读研究人员发现NVidia的Phys-X库使用x87 FP与SSE2.显然,对于速度超过精度的并行数据集来说,这将是次优的.然而,文章作者继续引用:

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

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

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

小智 25

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

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

顺便说一句,如果你遇到不准确的问题,你会想看看文章" 每个程序员应该知道浮点算术的内容 ",然后可以使用任意精度数学库(例如GMP).

  • 优化编译器已经足够糟糕了,但是尝试一个能够内联小方法的JIT(因此可以改变内存中的temps数量).有时我会调用这个方法并得到一个答案,有时候我会使用完全相同的参数调用相同的方法并得到不同的结果,这取决于JITter是否内联了调用!这是一个有趣的回归追踪. (8认同)
  • 是否有任何东西阻止8x87编译器将所有中间结果保持为80位值,无论它们是否适合寄存器,并指定它将这样做?这样的编译器的结果不能与任何其他同样编译器完全重现吗? (3认同)
  • 值得一提的是,80 位精度从未用于存储。它被故意设计为用作更高精度的中间表示,在存储结果时将转换回浮点数或双精度数。 (2认同)

sup*_*cat 5

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

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

应该有一些类型可以用来捕获和替换公共子表达式,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);
}
Run Code Online (Sandbox Code Playgroud)

不改变程序的语义.遗憾的是,ANSI C未能指定可用于some_type执行扩展精度计算的平台的任何类型,并且指责英特尔存在扩展精度类型而不是归咎于ANSI的拙劣支持变得更为常见.

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

  1. 将尾数,指数和可能的x符号解包到单独的寄存器中(指数和符号通常可以"双块")
  2. 同样打开包装.
  3. 使用较低的指数(如果有的话)右移该值的尾数,然后加上或减去这些值.
  4. 如果x和y具有不同的符号,则将尾数左移直到最左边的位为1并适当地调整指数.
  5. 将指数和尾数包装回双格式.
  6. 解压缩该临时结果.
  7. 打开z.
  8. 使用较低的指数(如果有的话)右移该值的尾数,然后加上或减去这些值.
  9. 如果较早的结果和z具有不同的符号,则将尾数左移直到最左边的位为1并适当地调整指数.
  10. 将指数和尾数包装回双格式.

使用扩展精度类型将允许消除步骤4,5和6.由于53位尾数太大而不适合少于4个16位寄存器或2个32位寄存器,因此使用64位尾数执行加法并不比使用53位尾数慢,所以使用扩展精度数学提供更快的计算,在支持适当类型以保存临时结果的语言中没有缺点.没有理由指责英特尔提供可能在当时是时尚执行浮点运算的FPU 非FPU芯片的最有效的方法.