释放和调试模式会给出不同的结果,除非我打印一个值

时间:2016-01-25 22:16:38

标签: c# floating-point precision

我有发布和调试代码可以产生截然不同的结果。由于我无法调试发布代码,因此我将值打印到文件中以比较两种模式。我已将问题本地化为几行代码。

var yFloat = topArcDetails.ArcPoints.GetInterpolatedYAt(point.X);
var tvdOnTopHorizon = (double) yFloat;
var yPointDouble = (double) point.Y;
deltaTvdTop = yPointDouble - tvdOnTopHorizon;
if (point.X == 2102.0 && point.Y > 1573.0 && point.Y < 1574.0)
{
    var message = String.Format("MD, TVD,         tvdOnTopHorizon, deltaTvdTop = {0}  {1}          {2}  {3}", point.X, point.Y, tvdOnTopHorizon, deltaTvdTop);
    //var message = String.Format("MD, TVD, yFloat, tvdOnTopHorizon, deltaTvdTop = {0}  {1}  {2}  {3}  {4}", point.X, point.Y, yFloat, tvdOnTopHorizon, deltaTvdTop);
    var oldOut = Console.Out;
    using (TextWriter w = File.AppendText("debugOutput.txt"))
    {
        Console.SetOut(w);
        Console.WriteLine(message);
    }
    Console.SetOut(oldOut);
}

请注意,我有两种可以打印到文件的消息选择。一条消息包含yFloat的值,另一条消息不包含。

如果我在没有调试的情况下在发布模式下运行,则第一条消息将给出:

MD, TVD,         tvdOnTopHorizon, deltaTvdTop = 2102  1573.135            1554.6184437573   18.5164439380169

第二条消息将给出:

MD, TVD, yFloat, tvdOnTopHorizon, deltaTvdTop = 2102  1573.135  1554.618  1554.61840820313  18.5164794921875

请注意,只打印yFloat的值会导致最后两个数字发生微小变化。在调试模式下,我看到print语句没有造成两个结果的区别:

MD, TVD, yFloat, tvdOnTopHorizon, deltaTvdTop = 2102  1573.135  1554.618  1554.61840820313  18.5164794921875
MD, TVD,         tvdOnTopHorizon, deltaTvdTop = 2102  1573.135            1554.61840820313  18.5164794921875

调试结果与打印yFloat的发布结果一致。我知道核心问题是代码混合浮点和双精度,这是别人的坏主意。虽然打印yFloat的价值可以解决问题,但我认为这不是很实用。它确实让我觉得必须有一些东西可以让发布版本与调试版本一致。

1 个答案:

答案 0 :(得分:2)

C#语言规范(您应该在C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC#\Specifications\1033这样的文件夹中有副本,或者您可以download it)在4.1.6节浮点类型中说明: / p>

  

可以以比操作的结果类型更高的精度执行浮点运算。例如,某些硬件体系结构支持“扩展”或“长双”浮点类型范围和精度比double类型,并使用此更高精度类型隐式执行所有浮点操作。只有在性能成本过高的情况下,才能使这种硬件架构以较低的精度执行浮点运算,而不是要求实现失去性能和精度,C#允许更高精度的类型用于所有浮点运算。除了提供更精确的结果外,这几乎没有任何可衡量的影响。但是,在x * y / z形式的表达式中,乘法产生的结果超出双范围,但随后的除法将临时结果带回双范围,表达式的计算结果更高范围格式可能会导致生成有限结果而不是无穷大。

(关键点是我用粗体表示的句子。)

基本上这意味着C#编译器可以自由地优化代码,但只要准确性变得更好而不是更差,它就会喜欢。

在您的情况下,虽然topArcDetails.ArcPoints.GetInterpolatedYAt(point.X)返回float,但结果用于计算double。显然,编译器在构建发布版本时将代码内联为优化,绕过转换为float。 (在x86版本与x64版本模式下编译时,您可能还会发现输出的差异。)

您可以显式转换该值,即使这看起来多余,

var yFloat = (float) topArcDetails.ArcPoints.GetInterpolatedYAt(point.X);

将强制编译器在使用之前将结果转换为float

因此,总之,您必须在准确性和可重复性之间进行选择。如果您最关心的是获得最准确的结果(可能还有更好的性能),请允许编译器进行优化。如果您更关心跨平台获得相同的结果,那么您需要手动添加额外的强制转换以强制中间计算为固定的位数。