【问题标题】:Multiplying a double value by 100.0 introduces rounding errors?将双精度值乘以 100.0 会引入舍入误差?
【发布时间】:2010-07-08 15:04:50
【问题描述】:

这就是我正在做的事情,99.999% 的时间都有效:

((int)(customerBatch.Amount * 100.0)).ToString()

金额值是双倍的。我正在尝试以便士的形式将值写入文本文件,以便传输到服务器进行处理。金额不超过 2 位精度。

如果您使用 580.55 作为金额,这行代码将返回 58054 作为字符串值。

此代码在 64 位网络服务器上运行。

有什么想法吗?

【问题讨论】:

  • 见这篇文章:docs.sun.com/source/806-3568/ncg_goldberg.html TL;DR:不要使用 float 或 double 来计算金钱
  • 该值以 Money 值的形式来自 SQL Server 数据库。对象包装类对这些值使用“double”,因此我无法更改 Amount 的类型。
  • 在处理货币时应尽可能使用小数而不是浮点数。
  • @Sophtware:您应该在包装类上提出一个错误,因为将货币值表示为双精度或浮点数是一个错误。

标签: c# .net math


【解决方案1】:

你真的应该使用小数来计算金钱。

((int)(580.55m * 100.0m)).ToString().Dump();

【讨论】:

  • +1,文档甚至建议使用小数进行财务/货币计算:msdn.microsoft.com/en-us/library/364x0z75.aspx
  • 忽略 .Dump() 方法。从 LinqPad 复制和粘贴。
  • 所以你建议使用:((int)((decimal)customerBatch.Amount * 100.0m)).ToString()
  • @Sophtware 您可以将强制转换为 int
  • 它可能适用于该值,但会有其他人不适用。一旦您的“包装器”将 SQL MONEY 转换为双倍损害,就会造成损害。你必须摆脱双打。
【解决方案2】:

您可以使用decimal 值进行准确计算。 Double 是浮点数,在计算过程中不保证精确。

【讨论】:

    【解决方案3】:

    我猜 580.55 正在转换为 58054.99999999999999999999999999...,在这种情况下,int 会将其向下舍入为 58054。您可能需要编写自己的函数,将您的金额转换为具有某种舍入的 int 或使这种情况不会发生的阈值。

    【讨论】:

    • 如果是这种情况,int 取整数部分,而不是四舍五入。那么像 ((int)(round(customerBatch.Amount * 100.0))).ToString() 这样的东西怎么样?
    • 是的,这可能行得通(我不熟悉 C#,也不知道它内置了一个圆形函数)。
    • 你在胡乱猜测。 58054.9999999... 不能比 580.55 更准确地表示为浮点数。
    • 我的意思是数字 580.55 可能没有被存储为 580.55。打印出来时可以表示为580.55。如果它被存储为小于 580.55 的数字(不管它打印为什么),它可能会通过转换为 int 来四舍五入。
    【解决方案4】:

    试试

    ((int)(Math.Round(customerBatch.Amount * 100.0))).ToString()

    【讨论】:

      【解决方案5】:

      由于诸如此类的舍入错误,您确实不应该使用双精度值来表示货币。

      相反,您可以考虑使用整数值来表示货币金额,以便准确地表示它们。要表示小数,您可以使用类似的技巧,将 580.55 存储为值 58055

      【讨论】:

        【解决方案6】:

        不,乘法不会引入舍入误差 但并非所有值都可以用浮点数表示。 x.55 就是其中之一)

        【讨论】:

        • 换句话说,在计算机上乘以浮点数确实会引入舍入误差。
        • 第一个舍入错误发生在 580.55 作为 580.5499999999xxx 存储在“double”中时。将它乘以 100 得到 58054.99999999xxx,这实际上并不比 580.5499999999xxx 准确。
        • 当然浮点类型的乘法会引入舍入误差。将两个 N 位尾数相乘需要大约 2N 位才能准确存储乘积。您必须丢失大约 N 位才能再次将结果存储回相同的类型。
        【解决方案7】:

        十进制比双精度具有更高的精度。试试十进制。

        http://msdn.microsoft.com/en-us/library/364x0z75%28VS.80%29.aspx

        【讨论】:

          【解决方案8】:

          我的建议是将值存储为整数的便士并取dollars_part = pennies / 100cents_part = pennies % 100。这将完全避免舍入错误。

          编辑:当我写这篇文章时,我没有看到你不能改变数字格式。最好的答案可能是使用其他人建议的圆形方法。

          编辑 2: 正如其他人指出的那样,最好使用某种定点十进制变量。这比我原来的解决方案要好,因为它将有关小数点位置的信息存储在它所属的值中,而不是在代码中。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-01-08
            • 2014-03-25
            • 2020-02-17
            • 1970-01-01
            • 2021-07-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多