【发布时间】:2014-02-06 11:05:59
【问题描述】:
我在使用 VS 2012 .net 4.5 编译为 64 位的 Windows 7 64 位商业产品中的测试代码遇到了一个非常奇怪的问题。
以下测试代码在单独的项目中执行时表现如预期(使用 NUnit 测试运行程序):
[Test]
public void Test()
{
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
if ((x * x + y * y + z * z) < (float.Epsilon))
{
return;
}
throw new Exception("This is totally bad");
}
测试返回为与
现在是奇怪的部分。当我在我们的商业产品的上下文中运行此代码时,此测试失败。我知道这听起来多么愚蠢,但我得到了抛出异常。我已经调试了这个问题,当我评估条件时它始终为真,但编译后的可执行文件仍然没有进入条件的真正分支并引发异常。
在商业产品中,这个测试用例只有在我的测试用例执行额外的设置代码(为集成测试而设计)时才会失败,其中初始化了一个非常大的系统(C#、CLI 和一个非常大的 C++ 部分)。我无法在此设置调用中进一步挖掘,因为它实际上引导了所有内容。
我不知道 C# 中有什么会影响评估。
额外的怪异: 当我用 float.Epsilon 与小于或等于比较时:
if ((x * x + y * y + z * z) <= (float.Epsilon)) // this works!
然后测试成功。我尝试仅与小于和 float.Epsilon*10 进行比较,但这不起作用:
if ((x * x + y * y + z * z) < (float.Epsilon*10)) // this doesn't!
尽管 Eric Lippert 等人的帖子我在谷歌上搜索该问题没有成功。倾向于浮动。Epsilon 我不完全理解对我的代码应用了什么效果。 是否是一些 C# 设置,大量的本地托管和反之亦然会影响系统。 CLI 中有什么东西?
编辑:还有一些发现: 我已使用此 MSDN 页面 http://msdn.microsoft.com/en-us/library/system.single.epsilon%28v=vs.110%29.aspx 中的 GetComponentParts 来可视化我的 mantiassa 结束指数,结果如下:
测试代码:
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
var res = (x*x + y*y + z*z);
Console.WriteLine(GetComponentParts(res));
Console.WriteLine();
Console.WriteLine(GetComponentParts(float.Epsilon));
没有我得到的整个 boostrap 链(测试通过)
0: Sign: 0 (+)
Exponent: 0xFFFFFF82 (-126)
Mantissa: 0x0000000000000
1.401298E-45: Sign: 0 (+)
Exponent: 0xFFFFFF82 (-126)
Mantissa: 0x0000000000001
有了我得到了完整的引导链(测试失败)
0: Sign: 0 (+)
Exponent: 0xFFFFFF82 (-126)
Mantissa: 0x0000000000000
0: Sign: 0 (+)
Exponent: 0xFFFFFF82 (-126)
Mantissa: 0x0000000000000
注意事项: float.Epsilon 在尾数中丢失了最后一位。
我看不出 C++ 中的 /fp 编译器标志如何影响 float.Epsilon 表示。
修改和最终裁决 虽然可以使用单独的线程来获取 float.Epsilon,但它在 FPU 字减少的线程上的行为将与预期不同。
在缩减的 FPU 字线程上,这是“thread-foreign”float.Epsilon 的输出
0: Sign: 0 (+)
Exponent: 0xFFFFFF82 (-126)
Mantissa: 0x0000000000001
请注意,最后一个尾数位如预期的那样为 1,但此浮点值仍将被解释为 0。这当然是有道理的,因为我们使用的浮点精度大于 FPU 字集,但它可能是对某人来说是个陷阱。
我决定转移到一台机器 fps,它按此处所述计算一次:https://stackoverflow.com/a/9393079/2416394(当然,移植到浮点数)
【问题讨论】:
-
听起来很像(编译器的)优化错误。您是否使用任何优化标志?
-
您的设置代码是否使用 DirectX?这可能会影响 FPU 设置:stackoverflow.com/questions/2847906/…
-
我看不出 C++ 中的 /fp 编译器标志如何影响 float.Epsilon 表示。 你问代码是否可以改变浮点数的行为。 /fp 文档链接到一个页面,该页面向您展示如何更改浮点控制字使程序能够根据平台更改浮点数学包中的精度、舍入和无穷大模式我>
-
能否修改设置代码以设置 D3DCREATE_FPU_PRESERVE?或者,您可以在新线程上执行有问题的代码吗?