5

我正在从Brainteasers 测试这段代码:

        double d1 = 1.000001;

        double d2 = 0.000001;

        Console.WriteLine((d1 - d2) == 1.0);

结果是“假”。当我更改数据类型时:

        decimal d1 = 1.000001M;

        decimal d2 = 0.000001M;

        decimal d3 = d1-d2;

        Console.WriteLine(d3 == 1);

程序写出正确答案:“真”。

这个问题只是在浮点数之后使用了 6 位数字。15位的精度发生了什么?

4

6 回答 6

28

这与精度无关——它与代表性的舍入误差有关。

System.Decimal能够表示大的浮点数,并显着降低发生任何舍入错误的风险,如您所看到的。 System.Single并且 System.Double无法做到这一点,并且会将这些数字四舍五入并产生类似于您在示例中看到的问题。

System.Decimal使用比例因子来保存小数位的位置,从而允许精确表示给定的浮点数,而System.SingleSystem.Double尽可能地近似您的值。

欲了解更多信息,请参阅System.Double

请记住,浮点数只能逼近十进制数,并且浮点数的精度决定了该数字逼近十进制数的准确度。默认情况下,Double 值包含 15 位精度的十进制数字,但内部维护最多 17 位数字。浮点数的精度有几个后果:

  • 在特定精度下看起来相等的两个浮点数可能不相等,因为它们的最低有效数字不同。

  • 如果使用十进制数,使用浮点数的数学或比较运算可能不会产生相同的结果,因为浮点数可能不完全接近十进制数。

于 2009-09-08T18:55:34.143 回答
8

通常,检查浮点值是否相等的方法是检查近似相等,即检查接近该数据类型的最小值(称为epsilon)的差异。例如,

if (Math.Abs(d1 - d2) <= Double.Epsilon) ...

这将测试d1d2是否由相同的位模式表示,给出或占用最低有效位。

更正 (2015 年 3 月 2 日添加)

经过进一步检查,代码应该更像这样:

// Assumes that d1 and d2 are not both zero
if (Math.Abs(d1 - d2) / Math.Max(Math.Abs(d1), Math.Abs(d2)) <= Double.Epsilon) ...

换句话说,取 和 之间的绝对差d1,然后按和d2中的最大值对其进行缩放,然后将其与 进行比较。d1d2Epsilon

参考资料
http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx
http://msdn.microsoft.com/en-us/library/system.double.aspx#Precision

于 2009-09-08T19:22:52.967 回答
6

decimal类型实现十进制浮点,而double二进制浮点。

十进制的优点是它在舍入方面的行为与人类一样,如果您使用十进制值对其进行初始化,那么该值将按照您的指定精确存储。这仅适用于有限长度且在可表示范围和精度内的十进制数。如果你用 1.0M/3.0M 初始化它,那么它不会像你在纸上写 0.33333-recurring 那样精确地存储。

如果用十进制初始化二进制 FP 值,它将从人类可读的十进制形式转换为很少是完全相同的值的二进制表示。

十进制类型的主要目的是实现金融应用,在 .NET 实现中它的精度也远高于双精度,但是二进制 FP 由硬件直接支持,因此比十进制 FP 运算快得多。

请注意,double 精确到大约 15 个有效数字而不是 15个小数位d1用 7 个有效数字值而不是 6 进行初始化,而d2只有 1 个有效数字。它们具有显着不同的量级这一事实也无济于事。

于 2009-09-08T19:57:38.107 回答
3

浮点数的想法是它们对于特定的位数并不精确。如果您想要那种功能,您应该查看decimal数据类型。

于 2009-09-08T18:55:42.630 回答
3

精度不是绝对的,因为不可能在十进制数和二进制数之间精确转换。

在这种情况下,0.1 十进制以二进制表示时会永远重复。它转换为 .000110011001100110011 ......并永远重复。再多的精度也无法准确地存储它。

于 2009-09-08T19:11:20.260 回答
2

避免比较浮点数的相等性。

于 2009-09-08T19:07:01.563 回答