Java Double 值 = 0.01 更改为 0.009999999999999787 [重复]

IT小君   2021-12-09T03:27:03

可能的重复:
为什么不使用 Double 或 Float 来表示货币?

我正在为我的高中课程用 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。为什么会这样?

评论(8)
IT小君

使用doublefor currency 是一个坏主意,为什么不使用 Double 或 Float 来表示货币?. 我建议使用BigDecimal或以美分进行每项计算。

2021-12-09T03:27:04   回复
IT小君

0.01 没有精确的浮点表示(就此而言,0.1 和 0.2 也没有)。

您可能应该使用整数类型进行所有数学运算,表示便士的数量。

2021-12-09T03:27:04   回复
IT小君

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

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

2021-12-09T03:27:04   回复
IT小君

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

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

2021-12-09T03:27:04   回复
IT小君

浮点数从来都不是 100% 准确的(不完全正确,请参阅下面的评论)。你永远不应该直接比较它们。也是整数舍入。最好的方法可能是以美分做,然后再转换成美元(1 美元 == 100 美分)。通过转换为整数,您将失去精度。

2021-12-09T03:27:04   回复
IT小君

它是一个浮点数(双)

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

我建议使用 int 值并对便士进行操作

2021-12-09T03:27:04   回复
IT小君

这是一个多次出现的问题。最重要的是,在使用二进制浮点数(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 可以精确表示)。

2021-12-09T03:27:05   回复
IT小君

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

2021-12-09T03:27:05   回复