dotNet十进制类型是否容易受到二进制比较错误的影响?

时间:2009-07-09 07:20:16

标签: c# .net comparison decimal

我每隔几个月偶然发现一个错误就是这个错误:

        double x = 19.08;
        double y = 2.01;
        double result = 21.09;

        if (x + y == result)
        {
            MessageBox.Show("x equals y");
        }
        else
        {
            MessageBox.Show("that shouldn't happen!");  // <-- this code fires
        }

你会认为代码显示“x等于y”,但事实并非如此 简短的解释是小数位表示为二进制数字,不适合双倍。

实施例: 2.625看起来像:

10.101

因为

1-------0-------1---------0----------1  
1 * 2 + 0 * 1 + 1 * 0.5 + 0 * 0.25 + 1 * 0,125 = 2.65

有些值(如19.08加上2.01的结果)不能用双精度表示。

一种解决方案是使用常量:

        double x = 19.08;
        double y = 2.01;
        double result = 21.09;
        double EPSILON = 10E-10;

        if ( x + y - result < EPSILON )
        {
            MessageBox.Show("x equals y"); // <-- this code fires
        }
        else
        {
            MessageBox.Show("that shouldn't happen!");
        }

如果我在第一个例子中使用decimal而不是double,则结果为“x equals y” 但我问自己如果这是因为“十进制”类型不容易受到这种行为的影响,或者只是在这种情况下工作,因为值“适合”到128位。

也许某人有比使用常数更好的解决方案?

顺便说一下。这不是dotNet / C#问题,它发生在我认为的大多数编程语言中。

3 个答案:

答案 0 :(得分:6)

只要您保持在适当范围内的自然小数值内,

十进制将是准确的。因此,如果您只是添加和减去,例如,不做任何会使所需数字范围偏差太大的任何事情(将非常大的数字添加到非常小的数字),您最终会得到容易比较的结果。乘法也可能没问题,但我怀疑使用它会更容易出错。

一旦你开始分裂,那就是问题可能发生的地方 - 特别是如果你开始除以包括2或5以外的素数因子的数字。

底线:在某些情况下它是安全的,但你真的需要很好地处理你将要执行的操作。

请注意,这不是帮助你的十进制的128位 - 它是数字表示为浮点十进制点值而不是浮点二进制点值。有关详细信息,请参阅有关.NET binary floating pointdecimal floating point的文章。

答案 1 :(得分:0)

System.Decimal只是一个具有不同基数的浮点数,所以从理论上讲,它仍然容易受到你指出的那种错误的影响。我想你刚刚发生了一个没有发生舍入的情况。更多信息here

答案 2 :(得分:0)

是的,.NET System.Double结构会受到您描述的问题的影响。

来自http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx

  

两个明显等效的浮点数可能无法比较,因为它们的最低有效位数不同。例如,C#表达式(double)1/3 ==(double)0.33333,不比较相等,因为左侧的除法运算具有最大精度,而右侧的常量仅精确到指定的数字。如果您创建一个自定义算法来确定是否可以将两个浮点数视为相等,则必须使用大于Epsilon常量的值来确定可接受的两个值的绝对差值,以使两个值相等。 (通常,差异幅度比Epsilon大很多倍。)