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

Jür*_*ock 2 .net c# 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
        }
Run Code Online (Sandbox Code Playgroud)

您可以假设代码显示"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
Run Code Online (Sandbox Code Playgroud)

并且一些值(如19.08加上2.01的结果)不能用double的位表示.

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

        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!");
        }
Run Code Online (Sandbox Code Playgroud)

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

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

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

Jon*_*eet 6

只要您保持在适当范围内的自然小数值内,十进制就是准确的.因此,如果您只是添加和减去,例如,不做任何会使所需数字范围偏差太大的任何事情(将非常大的数字添加到非常小的数字),您将最终得到容易比较的结果.乘法也可能没问题,但我怀疑它更难以得到它的不准确性.

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

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

请注意,这不是帮助你的十进制的128位 - 它是数字表示为浮动小数点值而不是浮动二进制点值.有关更多信息,请参阅有关.NET 二进制浮点十进制浮点的文章.