【问题标题】:Java Double value = 0.01 changes to 0.009999999999999787 [duplicate]Java Double 值 = 0.01 更改为 0.009999999999999787 [重复]
【发布时间】:2011-11-16 12:54:54
【问题描述】:

可能重复:
Why not use Double or Float to represent currency?

我正在为我的高中课程用 Java 编写一个基本的命令行程序。我们现在只使用变量。它用于计算购买后您找零中任何类型的纸币和硬币的数量。这是我的程序:

class Assign2c {
    public static void main(String[] args) {
        double cost = 10.990;
        int paid = 20;
        double change = paid - cost;
        int five, toonie, loonies, quarter, dime, nickel, penny;

        five = (int)(change / 5.0);
        change -= five * 5.0;

        toonie = (int)(change / 2.0);
        change -= toonie * 2.0;

        loonies = (int)change;
        change -= loonies;

        quarter = (int)(change / 0.25);
        change -= quarter * 0.25;

        dime = (int)(change / 0.1);
        change -= dime * 0.1;

        nickel = (int)(change / 0.05);
        change -= nickel * 0.05;

        penny = (int)(change * 100);
        change -= penny * 0.01;

        System.out.println("$5   :" + five);
        System.out.println("$2   :" + toonie);
        System.out.println("$1   :" + loonies);
        System.out.println("$0.25:" + quarter);
        System.out.println("$0.10:" + dime);
        System.out.println("$0.05:" + nickel);
        System.out.println("$0.01:" + penny);
    }
}

它应该一切正常,但在最后一步,当剩下 0.01 美元时,便士的数量应该是 1,但它是 0。在进入代码并将更改值输出到控制台几分钟后,我已经发现在最后一步当change = 0.01时,它变为0.009999999999999787。为什么会这样?

【问题讨论】:

  • 你不能用二进制正确表示所有小数。使用整数来执行这些操作,或者自己处理舍入。这个问题(或它的变体)已经在这里被问了数百次。这是一个很好的参考:download.oracle.com/docs/cd/E19957-01/806-3568/…
  • 只做mod更容易:P
  • Aaa我们又来了.....
  • 即使我从未使用过 BigDecimal(因为在那里我发现了与 MsExcell 的不同之处,但这只是我的问题)请阅读 [OTN 中的一个线程][1]、[1]:forums.oracle.com/forums/…

标签: java numbers double


【解决方案1】:

使用double 表示货币是个坏主意,Why not use Double or Float to represent currency?。我建议使用BigDecimal 或以美分计算。

【讨论】:

  • 以美分进行所有计算几乎总是比使用BigDecimal, IMO 更可取。
  • @Matt Ball 这有明显的定点算术问题,即你要么失去非整数的准确性,要么无论如何都需要其他解决方案。对于精度只有 1 美分的中间计算来说,这听起来像是灾难的秘诀。
  • @Voo:对于用户正在做的应用程序,一分钱的准确度就完美了。
  • @Oli Charlesworth 你的意思是一个没有实际用途的应用程序? ;) 当然这可能很好,但了解可能的解决方案的所有优点和缺点总是一个好主意(特别是如果你这样做的唯一原因是学习新东西)。并且以美分计算肯定不是 BigDecimals imo“几乎总是更可取的” - 如果准确性很重要(并且根据您所做的计算,最终结果可能会明显变化超过 1 美分)。我能看到的 bigdecimals 的唯一缺点是性能
【解决方案2】:

0.01 没有精确的浮点表示(0.1 和 0.2 也没有)。

你可能应该用整数类型来做所有的数学运算,代表便士的数量。

【讨论】:

  • 哇。对这个有感觉,但我不知道。然后我会用美分计算这个东西
  • +1 好主意,但并不总是适用恕我直言,'bigdecimal' 是不可能分解的方式。
【解决方案3】:

doubles 在内部不是十进制的,而是二进制的。它们的存储格式相当于“100101 乘以10000”(我在简化,但这是基本思想)。不幸的是,没有这些二进制值的组合可以精确到十进制 0.01,这就是其他答案的意思,当他们说浮点数不是 100% 准确,或者 0.01 在浮点数中没有精确表示时观点。

有多种方法可以解决这个问题,有些方法比其他方法更复杂。在您的情况下,最好的解决方案可能是在任何地方使用 ints 并保持以美分为单位的值。

【讨论】:

    【解决方案4】:

    正如其他人已经说过的,不要在财务计算中使用双精度数。

    这篇论文http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html(What Every Computer Scientist Should Know About Floating-Point Arithmetic)是了解计算机中浮点数学的必读文章。

    【讨论】:

      【解决方案5】:

      浮点数永远不会 100% 准确(不太正确,请参阅下面的 cmets)。你永远不应该直接比较它们。还有整数舍入。做到这一点的最好方法可能是以美分计算,然后再转换成美元(1 美元 == 100 美分)。通过转换为整数,您将失去精度。

      【讨论】:

      • 说“浮点数永远不会 100% 准确”是不正确的。
      • 我想我应该说你不能可靠地比较它们?
      • 0.5 是 100% 准确的。就像(或可以是)2 的大多数幂,以及它的大多数倍数(只要尾数可以包含分数的分子)。当您得到分母不是 2 的幂或分子太大而无法完全表示的分数时,就会遇到舍入错误。
      • 啊,是的,你是对的。谢谢。
      【解决方案6】:

      它是一个浮点数(双精度)

      你不应该用它来计算金钱......

      我建议使用 int 值并在 pennys 上进行操作

      【讨论】:

        【解决方案7】:

        这是一个多次出现的问题。底线是,在使用二进制浮点(Java 需要)的计算机上,只有分母是 2 的幂的分数才能被精确表示。

        十进制也会出现同样的问题。例如,1/3 变成 0.3333333...,因为 3 不是 10 的因数(我们使用十进制的底数)。同样是 1/17、1/19 等。

        在二进制浮点中,也会出现同样的基本问题。主要区别在于十进制,因为 5 是 10 的因数,所以 1/5 可以精确表示(1/5 的倍数也可以)。由于 5 不是 2 的因数,因此 1/5 无法用二进制浮点数精确表示。

        然而,与流行的看法相反,一些分数 可以被精确表示——特别是那些分母只有 2 作为质因数的分数(例如,1/8 或 1/256 可以表示准确地说)。

        【讨论】:

          【解决方案8】:

          我确定您知道某些分数的十进制表示会终止(例如 .01),而有些则不会终止(例如 2/3=.66666...)。问题是,哪些分数终止取决于您所处的基础;特别是,.01 不会以二进制结尾,因此即使 double 提供了很多精度,它也不能准确地表示 .01。正如其他人所说,使用 BigDecimal 或定点整数计算(将所有内容转换为美分)可能最适合货币;要了解有关浮点的更多信息,您可以从 The Floating-Point Guide- What Every Programmer Should Know About Floating-Point Arithmetic 开始。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-09-18
            • 2016-03-13
            • 2020-06-22
            • 1970-01-01
            • 1970-01-01
            • 2014-09-23
            相关资源
            最近更新 更多