添加两个双精度浮点数得到错误结果。

4

我正在使用以下代码,但在某些神秘的情况下,加法的结果并不如预期:

double _west = 9.482935905456543;
double _off = 0.00000093248155508263153;
double _lon = _west + _off;

// check for the expected result
Debug.Assert(_lon == 9.4829368379380981);
// sometimes i get 9.48293685913086 for _lon (which is wrong)

我在我的应用程序中使用了一些本地DLL,我怀疑其中某个DLL导致了这种“误差”,但我需要找出是哪一个。 有人能给我一个提示如何找到问题的根源吗?


我这里所说的也适用于双精度:http://stackoverflow.com/questions/1193554/trouble-with-float-in-objective-c/1193607#1193607 - malach
问题的根源是由于浮点精度设置错误引起的。有人将浮点精度设置为24位,这导致计算结果错误。使用_fpreset或_controlfp(MSVC运行时dll的函数)可以纠正此问题,但仍然不清楚是谁首先设置了这个精度? - Andreas Roth
另一个例子:0.8 + 0.4 = 1.2000000000000002 - Mahmoud Amini Arjmand
其他简单的例子:0.1 * 0.1 = 0.010000000000000002, 0.1 + 0.2 = 0.30000000000000004, 0.1 * 0.2 = 0.020000000000000004, 0.1 - 0.3 = -0.19999999999999998, 0.2 + 0.1 = 0.30000000000000004, 0.2 * 0.1 = 0.020000000000000004, 0.2 * 0.2 = 0.04000000000000001, 0.2 - 0.3 = -0.09999999999999998, 0.3 - 0.1 = 0.19999999999999998, 0.3 - 0.2 = 0.09999999999999998 :) - Robert Synoradzki
5个回答

12

使用double不完全准确, 建议使用decimal代替

使用double和float相比使用decimal的优点在于性能更高。


1
一个 Decimal 如何比 Double 提供更好的性能?您的 CPU 知道如何处理 SinglesDoubles,但是所有在 Decimal 上的操作都必须在内存中发生,这会更慢。 - Andrew Hare
3
我不知道我是怎么做到的,但我完全错读了你的回答 - 我很抱歉!将(-1)移除,并因指出性能问题而加上+1! - Andrew Hare

2

一开始我以为这是四舍五入误差,但实际上是你的断言有误。尝试将你计算的整个结果添加,不要任意进行四舍五入。

请尝试以下操作:

using System;

class Program
{
    static void Main()
    {
        double _west = 9.482935905456543;
        double _off = 0.00000093248155508263153;
        double _lon = _west + _off;

        // check for the expected result
        Console.WriteLine(_lon == 9.48293683793809808263153);       
    }
}

将来,当你需要避免通常与 System.Single 和 System.Double 类型相关的舍入误差时,最好使用 System.Decimal。然而,在这种情况下并非如此。通过在给定点任意舍入数字,你假设该类型也会在同一点舍入,但实际上并非如此。浮点数存储到其最大表示能力,只有达到阈值时才进行舍入。请保留 HTML 标签。

我尝试使用小数,但将双精度转换为小数失败了,因为小数自动舍入给定的值。如何避免这种情况? - Andreas Roth

1
问题是Double只有15-16位的精度(在您的示例中似乎需要更高的精度),而Decimal的精度为28-29位。 您是如何在Double和Decimal之间进行转换的?

0
您正在遭受四舍五入和精度问题的困扰。请参见this。使用 Decimal 可以解决问题。有关转换和取整的详细信息,请单击here
来自 MSDN:

当您将 float 或 double 转换为 decimal 时,源值将转换为十进制表示形式,并在必要时四舍五入到小数点后第 28 位之后的最近数字。根据源值的大小,可能会发生以下结果之一:

如果源值太小而无法表示为小数,则结果为零。

如果源值为 NaN(非数字)、无穷大或太大而无法表示为小数,则会引发 OverflowException。


0

在二进制系统中,无法准确地用浮点数表示十进制系统中的每个浮点数,这甚至与十进制数字有多“小”没有直接关系,有些数字只是不能很好地适应基于2的底数。

在大多数情况下,使用更长的位宽可以帮助解决问题,但并非总是如此。

要在Decimal(128位浮点数)精度中指定常量,请使用以下声明:

decimal _west = 9.482935905456543m;
decimal _off = 0.00000093248155508263153m;
decimal _lon = _west + _off;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接