Floating point x86/x64处理器使用哪些寄存器进行浮点运算?
x86/x64是否将SIMD寄存器用于高精度浮点运算或专用FP寄存器 我指的是高精度版本,不是常规的双精度。浮点单元(FPU)有用于80位浮点值的寄存器(采用Intel格式,即IEEE 754格式,略有变化) 各种SIMD单元(SSE、AVX等)具有较大的寄存器,可用于多种用途,但只有将其用作32位和64位浮点的指令。FPU(浮点单元)具有用于80位浮点值的寄存器(采用Intel格式,即IEEE 754格式,略有更改)Floating point x86/x64处理器使用哪些寄存器进行浮点运算?,floating-point,x86,64-bit,simd,cpu-registers,Floating Point,X86,64 Bit,Simd,Cpu Registers,x86/x64是否将SIMD寄存器用于高精度浮点运算或专用FP寄存器 我指的是高精度版本,不是常规的双精度。浮点单元(FPU)有用于80位浮点值的寄存器(采用Intel格式,即IEEE 754格式,略有变化) 各种SIMD单元(SSE、AVX等)具有较大的寄存器,可用于多种用途,但只有将其用作32位和64位浮点的指令。FPU(浮点单元)具有用于80位浮点值的寄存器(采用Intel格式,即IEEE 754格式,略有更改) 各种SIMD单元(SSE、AVX等)都有较大的寄存器,可用于多种用途,但只有
各种SIMD单元(SSE、AVX等)都有较大的寄存器,可用于多种用途,但只有将它们用作32位和64位浮点的指令。FPU堆栈仍然可用,并公开了@EricPostSchil指出的80位精度算术(不确定处理器是否仍然具有完整的逻辑,或者该部分是否在硬件级别进行了仿真)。它在GCC中以
long double
类型提供给开发人员。例如,为方法生成的程序集
long double f(long double a, long double b)
{
return a*b ;
}
将是
fldt 16(%rbp)
fldt 32(%rbp)
fmulp %st, %st(1)
这为使用此类数据提供了有用的元素,例如:
在编译没有SSE、AVX或其他矢量扩展的代码时,您的代码可能会使用80位FPU生成此类指令,并且可能会输出不同的值。下面是一个示例代码来说明:
double epstest(long double a, long double b)
{
long double y ;
y = a + b ;
y = y - a ;
return y ;
}
#include <cstdio>
int main()
{
double x = 1.0 ;
double y = 1e-17 ;
double z = x + y ;
z = z - x ;
printf ("double: %lf + %le - %lf = %le\n", x, y, x, z);
double res = epstest (x, y) ;
printf ("long double: %lf + %le - %lf = %le\n", x, y, x, res);
return 0 ;
}
在x86_64的软件中实现了更高的精度(超过长双精度)。FPU堆栈仍然可用,并公开了一个80位精度的算法,正如@EricPostDischil所指出的(不确定处理器是否仍然具有完整的逻辑,或者该部分是否在硬件级别进行了仿真)。在GCC中,开发人员可以使用
long double
类型使用它。例如,为方法生成的程序集
long double f(long double a, long double b)
{
return a*b ;
}
将是
fldt 16(%rbp)
fldt 32(%rbp)
fmulp %st, %st(1)
这为使用此类数据提供了有用的元素,例如:
在编译没有SSE、AVX或其他矢量扩展的代码时,您的代码可能会使用80位FPU生成此类指令,并且可能会输出不同的值。下面是一个示例代码来说明:
double epstest(long double a, long double b)
{
long double y ;
y = a + b ;
y = y - a ;
return y ;
}
#include <cstdio>
int main()
{
double x = 1.0 ;
double y = 1e-17 ;
double z = x + y ;
z = z - x ;
printf ("double: %lf + %le - %lf = %le\n", x, y, x, z);
double res = epstest (x, y) ;
printf ("long double: %lf + %le - %lf = %le\n", x, y, x, res);
return 0 ;
}
x86_64的软件实现了更高的精度(超过长双精度)。“高精度”都是由软件完成的。处理器除了x87 FPU中的双精度和扩展精度外一无所知。@神秘-我所知道的每个处理器都有分辨率超过64位的FP寄存器,用于内部操作,以最大限度地减少近似误差。这些“内部”软件无法访问寄存器。寄存器仅略大于53/64位,因此最终结果精确到53/64位。在舍入之前,乘法寄存器可能会大到106/128位。但您无法访问它们。您可以访问指令集公开的寄存器。这些寄存器包括x87 FPU/SSE/AVX。但是软件没有更高精度的版本可供使用。是的,在内部,处理器可能有更高精度的寄存器,但它们仅用于内部操作,这些操作在放回ISA可访问寄存器后,将全部四舍五入到53/64位。@神秘的正确四舍五入乘法是用三个ad实现的结果宽度的附加位,最后一个是专门连接的(“粘性位”)。向下滚动到“高精度”上的“粘性”都是由软件完成的。处理器除了x87 FPU中的双精度和扩展精度外一无所知。@神秘-我所知道的每个处理器都有分辨率超过64位的FP寄存器,用于内部操作,以最大限度地减少近似误差。这些“内部”软件无法访问寄存器。寄存器仅略大于53/64位,因此最终结果精确到53/64位。在舍入之前,乘法寄存器可能会大到106/128位。但您无法访问它们。您可以访问指令集公开的寄存器。这些寄存器包括x87 FPU/SSE/AVX。但是软件没有更高精度的版本可供使用。是的,在内部,处理器可能有更高精度的寄存器,但它们仅用于内部操作,这些操作在放回ISA可访问寄存器后,将全部四舍五入到53/64位。@神秘的正确四舍五入乘法是用三个ad实现的结果宽度的附加位,最后一个是专门连接的(“粘性位”)。从中可以看出,向下滚动到“粘性位”在x87上在当前一代x87 CPU上仍然具有高性能。例如,在Skylake上,
fadd
是3c延迟,每1c吞吐量一个(并且在端口5上运行!)。只有复杂的x87 INSN(如fsin)是微代码。即使是fsqrt
也很快。不过,80位浮点的加载/存储速度非常慢。相比之下,在Skylake上,addps
/addss
的延迟为4c,每0.5c吞吐量一个。(Skylake上的SSE/AVX add/sub/mul/fma都具有相同的性能,因为它们放弃了Broadwell之前提供的延迟较低的专用添加单元。)另外,您不必使用<代码> -O0<代码>或任何类似的哑。X8664 Sysv AbI指定<代码> long double /COD>是80位X87类型,因此使用该类型的代码需要有这么多的精度。@ PeterCordes,我基本上认为X87过时了。为什么会有人使用X87,例如用SkyLink处理器?@ ZBOXON:它是到目前为止的。e获得80位精度浮点的最快方法。此外,我不记得是哪个glibc函数,但我肯定我看到过一个使用x87的函数,专门用于手工编写的asm,用于一些简单的x87指令(不像fsin)。在尝试再次查找时,我确实找到了glibc的difftime
实现,它使用长双精度