为什么在调试和发布配置中进行浮点计算和强制类型转换会显示不同的结果?

9

这对我来说真是一个奇怪的bug,我花了很长时间才弄清楚发生了什么。为了简化事情并重现问题,只需使用VS2005创建一个空的win32控制台应用程序,并在主方法中使用以下代码:

float a = 411.00418f;
float b = 1.0f;
float c = 0.076279849f;
unsigned short result = (unsigned short)( (a-b)/c );
unsigned short result2 = (unsigned short)( (float)((a-b)/c) );
// Debug: 5374, 5375
// Release: 5374, 5374
printf("%d, %d\n", result, result2);  

为什么在调试/发布模式下,result2的值会显示不同?
1个回答

10

在MSVC中,默认的浮点模式是precise (/fp:precise),这意味着优化器可能会进行某些优化以提高精度或性能。

尝试将模式更改为strict (/fp:strict)。这将使编译器遵循严格的浮点运算规则,例如四舍五入。

(编辑:strict (/fp:strict)在这种情况下似乎不起作用...)

如果您查看已优化构建的反汇编代码,您会发现整个计算已被折叠和优化掉。

push    5374                    ; 000014feH
push    5374                    ; 000014feH
push    OFFSET ??_C@_07MHMABKGB@?$CFd?0?5?$CFd?6?$AA@
call    DWORD PTR __imp__printf
add esp, 12                 ; 0000000cH

编辑:我认为这是编译器优化器的错误。

在使用strict (/fp:strict) 时,以下代码会产生不同的结果:

float a = 411.00418f;
float b = 1.0f;
float c = 0.076279849f;

unsigned short result1 = (unsigned short)((float)((a-b)/c));

float d = (float)((a-b)/c);
unsigned short result2 = (unsigned short)( d );

输出:

5374, 5375
(float)((a-b)/c) 拆分为单独的赋值语句不应影响在启用strict (/fp:strict)的情况下的结果。

我认识一个工作在 MSVC 优化器上的人,我会给他发送一个错误报告。

更新:

这是他们的回复:

嗨 Alex,感谢您提供此错误报告。我将尝试在即将发布的 VC++ 版本中修复此问题,但可能无法实现。

顺便说一句,如果你打开 /arch:SSE2 ,这个 bug 就不会重现。由于我们正在默认情况下启用 /arch:SSE2 进行下一个 VC++ 发布(https://connect.microsoft.com/VisualStudio/feedback/details/688736/compiler-generates-sse-instructions-without-arch-sse),所以默认行为将显示该 bug 已经被修复了。 但是,如果您恢复旧的 FP 模型(throw /arch:IA32),则可能仍存在该 bug。

Eric

所以他们已经确认这是一个 bug。


我将“浮点模型”设置为“Strict (/fp:strict)”,结果仍然相同。我错过了什么吗? - AZ.
我也注意到了。我也在试图弄清楚它。在优化的构建中,优化器正在折叠所有计算。我认为这就是它不尊重严格舍入行为的地方。 - Mysticial
其实我的项目是在vs2010中使用C++/CLI,所以这也可能会受到影响? - AZ.
1
我正在尝试验证Strict (/fp:strict)是否确实像代码中所写的那样保证了严格的浮点数,然后再提交报告。这与CLI无关。优化器没有遵守严格的浮点数规则。 - Mysticial

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