奇怪的/fp浮点模型标志行为

5
我正在检查使用/fp:precise/fp:fast标志的代码。
根据/fp:preciseMSDN文档
在x86处理器上,编译器将对float类型的变量执行四舍五入,以适当的精度进行赋值、强制转换和传递参数给函数。这种舍入保证数据不会保留任何大于其类型容量的重要性。使用/ fp:precise编译的程序可能比未使用/ fp:precise编译的程序更慢、更大。 /fp:precise禁用了内置函数;而是使用标准运行时库例程。有关更多信息,请参见/Oi(生成内置函数)。
查看调用sqrtf的反汇编(使用/arch:SSE2,目标x86 / Win32平台):
0033185D  cvtss2sd    xmm0,xmm1  
00331861  call        __libm_sse2_sqrt_precise (0333370h)  
00331866  cvtsd2ss    xmm0,xmm0  

这个问题中,我相信现代的x86/x64处理器不使用80位寄存器(或者至少不鼓励使用),所以编译器会采用我认为是次优的方法,使用64位浮点数进行计算。由于内部函数被禁用,因此调用库函数sqrtf。
好的,很公平,这似乎符合文档所说的。
然而,当我编译x64架构时,出现了一些奇怪的情况:
000000013F2B199E  movups      xmm0,xmm1  
000000013F2B19A1  sqrtps      xmm1,xmm1  
000000013F2B19A4  movups      xmmword ptr [rcx+rax],xmm1  

计算不使用64位浮点数,而是使用指令集。据我所知,结果与使用/fp:fast标志完全相同。
为什么存在这两者之间的差异?/fp:precise在x64平台上不起作用吗?
现在,作为一项健全性检查,我在VS2010 x86中测试了相同的代码,并使用了/fp:precise/arch:SSE2。令人惊讶的是,使用了sqrtpd指令。
00AF14C7  cvtps2pd    xmm0,xmm0  
00AF14CA  sqrtsd      xmm0,xmm0  
00AF14CE  cvtpd2ps    xmm0,xmm0 

这里发生了什么?为什么VS2010使用内置函数而VS2012调用系统库?
针对x64平台测试VS2010的结果与VS2012类似(/fp:precise似乎被忽略)。
我没有访问旧版VS的权限,因此无法在这些平台上进行任何测试。
供参考,我正在Windows 7 64位上进行测试,使用的是Intel i5-m430处理器。

2
这真的很奇怪。我知道/fp:precise有时会导致编译器自行将中间值提升到更高的精度,但这并不能解释这里的绝对不一致性。 - Mysticial
请再说一遍?我相信x86架构没有80位寄存器。 - Pascal Cuoq
是的,措辞有些奇怪。更新以澄清一般建议不要使用它们。 - helloworld922
@PascalCuoq 回应您(已删除)答案上的评论。有3种模式,precisefaststrict。据我所知,precise意味着编译器将尽可能生成精确的结果。(可能以性能为代价)fast是不言自明的,与GCC的ffast-math相同。strict严格遵循IEEE标准。 - Mysticial
1个回答

3

首先,您应该阅读this关于中间浮点精度的优秀博客文章。该文章仅涉及Visual Studio生成的代码(但这正是您问题的关键所在)。现在来看看例子:

0033185D  cvtss2sd    xmm0,xmm1  
00331861  call        __libm_sse2_sqrt_precise (0333370h)  
00331866  cvtsd2ss    xmm0,xmm0  

此汇编代码是使用/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

第二个示例已经编译为x64(amd64)平台,该平台的行为完全不同!根据文档:
为了提高性能,中间操作是在任一操作数的最宽精度上计算,而不是在可用的最宽精度上计算。
因此,计算将在内部使用单精度。我认为他们还决定尽可能使用Intrinsics,因此在x64平台上,/fp:precise和/fp:fast之间的差异略小。新的行为导致更快的代码,并且它使程序员更加控制发生的事情(他们能够改变游戏规则,因为兼容性问题对于新的x64平台不是问题)。不幸的是,这些更改/差异没有在文档中明确说明。
00AF14C7  cvtps2pd    xmm0,xmm0  
00AF14CA  sqrtsd      xmm0,xmm0  
00AF14CE  cvtpd2ps    xmm0,xmm0 

最后,这个例子是使用Visual Studio 2010编译的,我认为他们在sqrt函数上意外地使用了内部函数,而他们应该不这样做(至少对于/fp:precise模式来说),但是他们在Visual Studio 2012中决定改变/修复这种行为(参见此处)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接