【问题标题】:Is a double really unsuitable for money?双倍真的不适合赚钱吗?
【发布时间】:2010-09-23 22:08:36
【问题描述】:

我总是在 c# 中告诉 double 类型的变量不适合钱。所有奇怪的事情都可能发生。但我似乎无法创建一个示例来演示其中一些问题。谁能提供这样的例子?

(编辑;这篇文章最初被标记为C#;一些回复提到了decimal的具体细节,因此意味着System.Decimal)。

(编辑 2:我特别要求一些 c# 代码,所以我认为这不仅仅是语言不可知论)

【问题讨论】:

标签: c# language-agnostic decimal currency


【解决方案1】:

非常非常不合适。使用小数。

double x = 3.65, y = 0.05, z = 3.7;
Console.WriteLine((x + y) == z); // false

(来自 Jon 的页面 here 的示例 - 推荐阅读;-p)

【讨论】:

  • 该死,如果我知道我自己的页面上有一个示例,我就不会想出不同的示例了 ;)
【解决方案2】:

四舍五入会有效地导致奇怪的错误。此外,与精确值的比较非常棘手 - 您通常需要应用某种 epsilon 来检查实际值是否“接近”特定值。

这是一个具体的例子:

using System;

class Test
{
    static void Main()
    {
        double x = 0.1;
        double y = x + x + x;
        Console.WriteLine(y == 0.3); // Prints False
    }
}

【讨论】:

  • 如果您使用的服务返回您无法控制的双倍货币值,在将它们转换为十进制时是否需要考虑一些问题?精度损失等...
  • @vikingben:绝对 - 从根本上说,这是一种错误的做事方式,你需要弄清楚如何最好地解释数据。
【解决方案3】:

是的,它不合适。

如果我没记错的话 double 大约有 17 个有效数字,所以通常舍入错误会发生在小数点后面很远的地方。大多数财务软件在小数点后使用 4 位小数,剩下 13 位小数可供使用,因此您可以用于单个操作的最大数字仍然远远高于美国国债。但舍入误差会随着时间的推移而增加。如果您的软件运行了很长时间,您最终会开始亏损。某些操作会使情况变得更糟。例如,将大量添加到少量会导致精度显着下降。

您需要定点数据类型来进行货币操作,大多数人不介意您在这里和那里损失一分钱,但会计师与大多数人不同..

编辑
根据这个网站http://msdn.microsoft.com/en-us/library/678hzkk9.aspx 双打实际上有 15 到 16 位有效数字,而不是 17。

@Jon Skeet decimal 比 double 更合适,因为它具有更高的精度,28 或 29 位有效小数。这意味着累积舍入误差变得显着的可能性较小。像 Boojum 提到的定点数据类型(即代表美分或 100 美分的整数)实际上更适合。

【讨论】:

  • 请注意,在 .NET 中建议使用的类型 System.Decimal 仍然是浮点类型 - 但它是浮点小数点而不是浮点二进制点。我怀疑,在大多数情况下,这比固定精度更重要。
  • 这正是问题所在。现在的货币通常是十进制的。然而,早在美国股市十进制化之前,二进制分数就在使用中(我开始在某一时刻看到 256 分甚至 1024 分),因此对于股票价格来说,双倍比小数更合适!十进制前的英镑将是一个真正的痛苦,尽管 960 英镑兑英镑;这既不是十进制也不是二进制,但它确实为简单的分数提供了大量的质因数。
  • 比十进制浮点更重要的是,decimal 表达式 x + 1 != x 始终为真。此外,它保留了精度,因此您可以区分11.0
  • @Gabe:这些属性只有在对一个人的值进行缩放以使值 1 代表最小的货币单位时才有意义。 Decimal 值可能会丢失小数点右侧的精度而不表示任何问题。
  • double 有 15.9 个有效十进制数字,仅考虑整数值。小数点后的情况取决于值。
【解决方案4】:

由于decimal 使用 10 倍数的比例因子,因此可以精确表示 0.1 之类的数字。本质上,十进制类型将其表示为 1 / 10 ^ 1,而 double 将其表示为 104857 / 2 ^ 20(实际上它更像 really-big-number / 2 ^ 1023)。

decimal 可以精确地表示任何基数为 10 的值,最多 28/29 个有效数字(如 0.1)。 double 不能。

【讨论】:

  • 十进制没有 96 位有效数字。它有 96 个有效的。十进制有大约 28 位有效数字。
  • 你说的是哪种语言的十进制类型?还是所有支持这种类型的语言都以完全相同的方式支持它?可能要指定。
  • @Adam - 这篇文章最初有 C# 标签,所以我们专门讨论 System.Decimal。
  • 糟糕,乔恩发现了!已更正。亚当,根据问题,我说的是 C#。有没有其他语言有一种叫做十进制的类型?
  • @Richard:嗯,所有基于 .NET 的语言都可以,因为 System.Decimal 不是唯一的 C# 类型,它是 .NET 类型。
【解决方案5】:

我的理解是,大多数金融系统都使用整数来表示货币——即,以美分计算一切。

IEEE 双精度实际上可以 精确地表示-2^53 到+2^53 范围内的所有整数。 (Hacker's Delight,第 262 页)如果您只使用加法、减法和乘法,并将所有内容保持在此范围内的整数,那么您应该不会看到精度损失。不过,我会非常警惕除法或更复杂的操作。

【讨论】:

  • 如果你只打算使用整数,为什么不使用整数类型开始呢?
  • Heh - int64_t 可以精确地表示 -2^63 到 +2^63-1 范围内的所有整数。如果您只使用加法、减法和乘法,并将所有内容保持在此范围内的整数,那么您应该不会看到精度损失。但是,我会非常警惕分裂。
  • 一些(唉?)仍在使用的过时系统支持double,但不支持任何 64 位整数类型。我建议以double 的形式执行计算,将任何语义要求的舍入都按比例缩放到整个单位,这往往是最有效的方法。
【解决方案6】:

当您不知道自己在做什么时使用 double 是不合适的。

“double”可以表示一万亿美元的金额,误差为 1/90 美分。因此,您将获得高度精确的结果。想计算将一个人送上火星并让他活着回来需要多少钱?双倍会做得很好。

但是对于金钱,通常有非常具体的规则,即某种计算必须给出某种结果,而不是其他结果。如果您计算的金额非常非常接近 98.135 美元,那么通常会有一条规则确定结果应该是 98.14 美元还是 98.13 美元,您必须遵循该规则并获得所需的结果.

根据您居住的地方,使用 64 位整数来表示美分、便士或戈比或您所在国家/地区的任何最小单位通常都可以正常工作。例如,代表美分的 64 位有符号整数可以代表高达 92,223 万亿美元的价值。 32 位整数通常不适合。

【讨论】:

    【解决方案7】:

    没有双精度数总是会出现舍入错误,如果您在 .Net 上使用“十进制”...

    【讨论】:

    • 小心。 任何 浮点表示都会有舍入错误,包括小数。只是小数会以人类直观的方式进行四舍五入(并且通常适用于金钱),而二进制浮点则不会。但对于非金融数字运算,双精度通常比十进制好得多,即使在 C# 中也是如此。
    【解决方案8】:

    实际上,浮点 double 非常适合表示金额,只要您选择合适的单位即可。

    http://www.idinews.com/moneyRep.html

    定点long也是如此。要么消耗 8 个字节,当然比 decimal 项消耗的 16 个字节更可取。

    某事是否有效(即产生预期和正确的结果)与投票或个人偏好无关。一项技术要么有效,要么无效。

    【讨论】:

    • 链接你写的一篇文章,它不同意几十年来的常见做法和专家选项,即浮点不适合金融交易表示,需要比单个页面多一点备份。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 2016-07-31
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多