(如何)调试会改变程序的工作流程?

(How) does debugging change the workflow of the program?

提问人:wkl 提问时间:4/6/2017 最后编辑:wkl 更新时间:4/6/2017 访问量:90

问:

请考虑以下简单程序:

var dblMax = Double.MaxValue;

var result = (dblMax * 1000) / 1800;
Console.WriteLine(result);

当我在调试模式下构建它并运行 (Ctrl+F5) 或调试 (F5) 它时,它会打印 .9.987140856842E+307

当我切换到发布模式并运行 (Ctrl+F5) 它时,它会无限打印。8

我知道这种差异是由于在发布模式下完成的一些编译器优化造成的。

但是,如果我在发布模式下调试 (F5) 相同的版本,它会再次打印!9.987140856842E+307

我正在调试的事实如何改变计算结果?

编辑:

我不问为什么调试模式和发布模式会产生不同的结果。我想知道为什么发布模式会产生不同的结果,具体取决于我是否调试 (F5) 或是否 (Ctrl+F5)。

C# Visual-Studio-2010 调试

评论

0赞 Panagiotis Kanavos 4/6/2017
执行不会改变,至少不会以您想象的方式改变。(∞)的结果是什么?如果∞除以 1800,你会得到什么?dblMax * 1000
0赞 wkl 4/6/2017
@PanagiotisKanavos我对无限的结果并不感到惊讶。令我惊讶的是,相同的版本会根据我是否调试它而打印出不同的结果。
0赞 Panagiotis Kanavos 4/6/2017
如果我说我还不能重现这个,你会感到惊讶吗?
0赞 wkl 4/6/2017
事实上,我会感到惊讶(假设你已经尝试过)。我在 VS 2010 Professional 中的新 c# 控制台应用程序中运行了这段代码。
1赞 daniell89 4/6/2017
@PanagiotisKanavos,我正在使用 VS2013、.net 4.6.1,我可以重现该问题......

答:

3赞 NicoRiff 4/6/2017 #1

这与浮点精度密切相关。在调试模式下,编译器使用 80 位精度。在发布模式下,编译器使用 64 位截断结果。

何时发生这种情况取决于多个配置、设置和环境变量。例如,您可以关闭发布模式配置的优化。这应该会有所帮助。

看看这个 Jon Skeet 的答案: https://stackoverflow.com/a/18417944/637840

评论

0赞 wkl 4/6/2017
但是,为什么发布版本在使用 F5 或 Ctrl F5 启动时具有不同的输出?
0赞 Panagiotis Kanavos 4/6/2017
这是另一回事——无论之后发生什么,乘法都应该得到无穷大。除非找到安装 .NET 4.5 的计算机,否则无法重现。OP 虽然使用 VS 2010 和 .NET 2.0 或 4.0 - 这些都是非常非常旧的版本
0赞 NicoRiff 4/6/2017
@wkl CTRL F5 将在没有调试器的情况下运行应用程序。F5 将在发布模式下进行调试。
0赞 wkl 4/6/2017
@NicoRiff没错。因此,两者都以发布模式运行。但它们仍然产生不同的输出。
3赞 Lasse V. Karlsen 4/6/2017
调试器将人为地延长局部变量的生存期,以确保您可以在各个点检查它们,这可以将值的存储位置从寄存器(精度较高)更改为内存位置(精度较低)。
5赞 Lasse V. Karlsen 4/6/2017 #2

调试时,JITter 的行为会有所不同。

首先,在许多情况下,局部变量的生存期会发生变化,以便进行检查。请考虑在计算期间使用变量后命中断点。如果 JITter 知道该变量不会在表达式之后使用,并且它没有延长变量的生存期,则最终可能无法查看该变量,这是调试的核心功能。

JITer 非常清楚地知道变量何时有用。如果在此期间有寄存器可用,它最终可能会使用此寄存器来存储变量。

但是,在附加调试器的情况下,它可能会决定改用内存位置,因为生存期发生了足够的变化,因此寄存器不可用于该部分代码。

CPU 的浮点寄存器比相应的浮点存储格式具有更高的精度,这意味着一旦您将一个值从寄存器中取出并存入内存,或者只是将其一直存储在内存中,您将体验到较低的精度。

RELEASE 和 DEBUG 构建之间的差异最终可能决定了这些事情,调试器的存在也是如此。

此外,不同的 .NET 运行时版本之间可能存在差异,这可能会影响这一点。


正确编写浮点代码需要深入了解您正在尝试执行的操作以及机器和平台的各个部分将如何干扰。我会尽量避免编写这样的代码。