C# 浮点差异取决于调试生成的运行方式

C# 浮点差异取决于调试生成的运行方式,c#,debugging,visual-studio-2012,floating-point,testdriven.net,C#,Debugging,Visual Studio 2012,Floating Point,Testdriven.net,无论是否在调试器下运行,我都在使用调试构建并在同一台计算机上获得不同的结果。我正在使用优秀的TestDriven.Net来运行单元测试 使用TestDriven.Net或外部NUnit运行程序“运行”会产生相同的结果 使用TestDriven.Net的“使用调试器运行”会产生不同的结果 代码是 一种复杂的迭代网格变形例程,涉及浮点精度极限下的大量计算 C#,VS2012瞄准.Net 3.5 单螺纹 仅调试生成,尚未生成发布版本 同一台机器,没有省电\speedstep或我知道的其他功能 香

无论是否在调试器下运行,我都在使用调试构建并在同一台计算机上获得不同的结果。我正在使用优秀的TestDriven.Net来运行单元测试

  • 使用TestDriven.Net或外部NUnit运行程序“运行”会产生相同的结果
  • 使用TestDriven.Net的“使用调试器运行”会产生不同的结果
代码是

  • 一种复杂的迭代网格变形例程,涉及浮点精度极限下的大量计算
  • C#,VS2012瞄准.Net 3.5
  • 单螺纹
  • 仅调试生成,尚未生成发布版本
  • 同一台机器,没有省电\speedstep或我知道的其他功能
  • 香草C#-无不安全代码、非托管库、平台调用等
  • 没有调试器检查代码或奇怪的第三方库
我没有追溯到第一个差异(没有调试器是很棘手的!),但是考虑到代码的迭代性,它的输入敏感,如果有足够的时间,最小的差异将增长到显著的比例

我知道编译器、平台和体系结构之间的fp再现性是多么脆弱,但失望地发现调试器是导致这种情况的因素之一

我是否必须接受这一事实,或者你能提供什么建议

我是否必须接受这一事实,或者你能提供什么建议

你必须接受这一事实。浮点代码可以在不同的情况下进行不同的优化。特别是,在某些情况下,JIT编译器可以使用精度/精度更高的表示(例如80位浮点)进行操作。JIT编译器执行此操作的情况将取决于体系结构、优化设置等。对于如何处理变量(以及是否为局部变量),可能存在许多微妙之处,这些都会影响此操作。在调试器下运行通常会对JIT优化设置产生非常显著的影响—不仅仅是对浮点值—所以我对此一点也不感到惊讶


如果您使用某个容差执行浮点比较,那么应该没问题——无论如何,对浮点类型进行精确的相等性比较很少是一个好主意。当然,在差异变得显著的情况下,你可能实际上在进行一个不平等比较,但我很少把这当作一个问题。

谢谢Jon。该领域是计算几何,涉及的代码正在清除退化情况、共线性、重合性等。其全部基于公差,但数量和迭代性质最终会产生显著的细微差异。是否无法使整个平台(JIT、调试器,…)当计算涉及单精度和双精度时,是否使用SSE2指令?在所有语言中,浮点再现性的问题在C语言中都被认为是可以解决的,这要归功于这些指令。@PascalCuoq:不仅仅是要使用哪些指令。考虑一个本地的代码>代码>双/Cux>变量,它可以存储在一个80位登记器中,因此当有重复操作时,如果它以64位存储在正常内存中,则可以得到不同的值。删除该功能是可能的(就像Java对strictfp所做的那样),但要以优化为代价。SSE2指令有自己的16个“XMM”寄存器,它们可以用来精确存储浮点或双精度。即使预先存在的ABI要求使用旧的FP堆栈,只要计算是专门使用SSE2指令进行的,当这些旧寄存器用于根据ABI传递参数时,它们只包含可表示的
double
s,这些寄存器在读取时不会丢失。@PascalCuoq:我明白了。不幸的是,我没有任何内部信息来知道为什么这些没有在CLR中使用。但是,这些指令可能不是跨体系结构的通用指令?请记住,CLR也在ARM上运行-ARM有类似的功能吗?