Strange/fp浮点模型标志行为

hel*_*922 5 c floating-point sse visual-studio-2010 visual-studio-2012

我正在检查一些使用/fp:precise/fp:fast标志的代码.

按照MSDN文档/fp:precise:

使用/ fp:精确地在x86处理器上,编译器将对float类型的变量执行舍入,以便为赋值和强制转换以及将参数传递给函数时具有适当的精度.这种舍入保证了数据不会保留大于其类型容量的任何重要性.使用/ fp:precise编译的程序可以比没有/ fp:precise编译的程序更慢更大./ fp:exact禁用内在函数; 而是使用标准的运行时库例程.有关更多信息,请参阅/ Oi(生成内部函数).

查看对sqrtf(调用/arch:SSE2目标x86/Win32平台)的调用的反汇编:

0033185D  cvtss2sd    xmm0,xmm1  
00331861  call        __libm_sse2_sqrt_precise (0333370h)  
00331866  cvtsd2ss    xmm0,xmm0  
Run Code Online (Sandbox Code Playgroud)

这个问题我相信现代的x86/x64处理器不使用80位寄存器(或者至少不鼓励使用它们),所以编译器会做我认为是下一个最好的事情并用64位双精度计算.因为内在函数被禁用,所以调用了一个库sqrtf函数.

好吧,相当公平,这似乎符合文档所说的内容.

但是,当我为x64 arch编译时,会发生一些奇怪的事情:

000000013F2B199E  movups      xmm0,xmm1  
000000013F2B19A1  sqrtps      xmm1,xmm1  
000000013F2B19A4  movups      xmmword ptr [rcx+rax],xmm1  
Run Code Online (Sandbox Code Playgroud)

不使用64位双精度执行计算,并且正在使用内在函数.据我所知,结果与/fp:fast使用标志的结果完全相同.

为什么两者之间存在差异?难道/fp:precise根本无法与x64平台工作?

现在,作为一个完整性检查,我用/fp:precise和测试了VS2010 x86中的相同代码/arch:SSE2.令人惊讶的是,sqrtpd内在被使用了!

00AF14C7  cvtps2pd    xmm0,xmm0  
00AF14CA  sqrtsd      xmm0,xmm0  
00AF14CE  cvtpd2ps    xmm0,xmm0 
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?为什么VS2010在VS2012调用系统库时会使用内在函数?

针对x64平台的VS2010测试结果与VS2012相似(/fp:precise似乎被忽略).

我无法访问任何旧版本的VS,因此我无法在这些平台上进行任何测试.

作为参考,我正在使用Intel i5-m430处理器在Windows 7 64位中进行测试.

小智 3

首先,您应该阅读这篇关于中间浮点精度的非常好的博客文章。本文仅处理 Visual Studio 生成的代码(但这就是您的问题的全部内容)。现在来看例子:

0033185D  cvtss2sd    xmm0,xmm1  
00331861  call        __libm_sse2_sqrt_precise (0333370h)  
00331866  cvtsd2ss    xmm0,xmm0  
Run Code Online (Sandbox Code Playgroud)

/fp:precise /arch:SSE2此汇编代码是针对 x86 平台生成的。根据文档,精确的浮点模型促使所有计算在 x86 平台上内部加倍。它还阻止使用内在函数(我想您已经阅读了此信息)。因此,代码首先从 float 到 double 的转换,然后是双精度 sqrt 调用,最后将结果转换回 float。

000000013F2B199E  movups      xmm0,xmm1  
000000013F2B19A1  sqrtps      xmm1,xmm1  
000000013F2B19A4  movups      xmmword ptr [rcx+rax],xmm1
Run Code Online (Sandbox Code Playgroud)

第二个示例是针对 x64 (amd64) 平台编译的,该平台的行为完全不同!根据文档:

出于性能原因,中间运算以任一操作数的最宽精度而不是可用的最宽精度进行计算。

因此,计算将在内部以单精度完成。我认为他们还决定尽可能使用内在函数,因此/fp:precise和之间的差异在 x64 平台上/fp:fast要小一些。新的行为会产生更快的代码,并使程序员能够更好地控制到底发生的事情(他们能够改变游戏规则,因为新的 x64 平台不关心兼容性问题)。不幸的是,这些更改/差异并未在文档中明确说明。

00AF14C7  cvtps2pd    xmm0,xmm0  
00AF14CA  sqrtsd      xmm0,xmm0  
00AF14CE  cvtpd2ps    xmm0,xmm0 
Run Code Online (Sandbox Code Playgroud)

最后,最后一个示例是使用 Visual Studio 2010 编译器编译的,我认为他们不小心使用了 sqrt 的内部函数,而他们最好不要使用内部函数(至少对于/fp:precise模式),但他们决定在 Visual Studio 2012 中更改/修复此行为再次(参见此处)。