C#中的某些内容可以在运行时更改浮点比较行为吗?[64]

Sam*_*uel 25 .net c#

我在Windows 7 64位的商业产品中使用我们的测试代码遇到了一个非常奇怪的问题,VS 2012 .net 4.5编译为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");
}
Run Code Online (Sandbox Code Playgroud)

测试返回,因为与<float.Epsilon的比较对于x,y和z始终为true,为0.0f.

现在这里是奇怪的部分.当我在商业产品的上下文中运行此代码时,此测试失败.我知道这听起来多么愚蠢,但我得到了异常抛出.我调试了这个问题,当我评估条件时,它总是正确但编译后的可执行文件仍然没有进入条件的真正分支并抛出异常.

在商业产品中,当我的测试用例执行额外的设置代码(专为集成测试而设计)时,这个测试用例只会失败,其中一个非常大的系统被初始化(C#,CLI和一个非常大的C++部分).我无法在这个设置调用中进一步挖掘,因为它几乎可以引导一切.

我不知道C#中会有任何影响评估的内容.

额外的怪异:当我与float.Epsilon的较小或相等比较时:

if ((x * x + y * y + z * z) <= (float.Epsilon)) // this works!
Run Code Online (Sandbox Code Playgroud)

然后测试成功.我试过比较只有less-than和float.Epsilon*10,但这不起作用:

if ((x * x + y * y + z * z) < (float.Epsilon*10)) // this doesn't!
Run Code Online (Sandbox Code Playgroud)

我一直在谷歌上搜索这个问题,尽管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));
Run Code Online (Sandbox Code Playgroud)

没有整个boostrap链我得到(测试通过)

0: Sign: 0 (+)
   Exponent: 0xFFFFFF82 (-126)
   Mantissa: 0x0000000000000

1.401298E-45: Sign: 0 (+)
   Exponent: 0xFFFFFF82 (-126)
   Mantissa: 0x0000000000001
Run Code Online (Sandbox Code Playgroud)

完整的自举链我得到(测试失败)

0: Sign: 0 (+)
   Exponent: 0xFFFFFF82 (-126)
   Mantissa: 0x0000000000000

0: Sign: 0 (+)
   Exponent: 0xFFFFFF82 (-126)
   Mantissa: 0x0000000000000
Run Code Online (Sandbox Code Playgroud)

需要注意的事项: float.Epsilon在其尾数中丢失了最后一点.

我看不出C++中的/ fp编译器标志如何影响float.Epsilon表示.


编辑和最终裁决 虽然可以使用单独的线程来获取float.Epsilon,但它在FPU字减少的线程上的行为将与预期的不同.

在简化的FPU字线程上,这是"线程外部"float.Epsilon的输出

0: Sign: 0 (+)
   Exponent: 0xFFFFFF82 (-126)
   Mantissa: 0x0000000000001
Run Code Online (Sandbox Code Playgroud)

请注意,最后一个尾数位是预期的1,但是这个浮点值仍将被解释为0.这当然有意义,因为我们使用的浮点精度比FPU字集大,但它可能是一个陷阱有人.

我决定转到计算机fps,计算一次如下所述:https://stackoverflow.com/a/9393079/2416394(移植到浮动,当然)

Hen*_*rik 11

已知DirectX可以修改FPU设置.请参阅此相关问题:浮点精度可以与线程相关吗?

您可以通过D3DCREATE_FPU_PRESERVE在调用CreateDevice或在新线程上执行浮点代码时指定标志来告诉DirectX保留FPU设置.