奇怪的Java行为:为什么添加两个小数点后面的两个小数会产生双倍超过两位小数?

Mat*_*ros 17 java

如果我有一个双精度数组,每个都有两个小数位,通过一个循环完全添加它们,并打印出总数,出来的是一个超过两位小数的数字.这很奇怪,因为从理论上讲,添加两个数字,每个数字只有2个,只有2个小数位,所以不会产生一个超过百分位数的非零数字的数字.

尝试执行此代码:

double[] d = new double[2000];
for (int i = 0; i < d.length; i++) {
    d[i] = 9.99;
}

double total = 0,00;
for (int i = 0; i < d.length; i++) {
    total += d[i];
    if (("" + total).matches("[0-9]+\\.[0-9]{3,}")) { // if there are 3 or more decimal places in the total
        System.out.println("total: " + total + ", " + i); // print the total and the iteration when it occured
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的电脑中,打印出:

total: 59.940000000000005, 5
Run Code Online (Sandbox Code Playgroud)

如果我将总数舍入到小数点后两位,那么如果我在计算器上手动添加9.99六次,我会获得相同的数字.但是为什么会发生这种情况呢?额外的小数位从何而来?我做错了什么或(我怀疑这很可能)这是一个Java错误吗?

And*_*dre 14

您是否熟悉分数的基数10到基数2转换(十进制到二进制)?如果没有,请查阅.

然后你会看到尽管在基数10中9.99看起来很正常,但它在二进制中看起来并不那么好看; 它看起来像重复的十进制,但是二进制.我确定你之前看过重复的小数,对吗?它并没有结束.但Java(或任何语言)必须将无限的数字序列保存为有限的字节数.这就是额外数字出现的时候.当您将截断的二进制文件转换回十进制时,您实际上正在处理不同的数字.存储在变量中的数字不是9.99,就像9.9999999991(只是一个例子,我没算算数学).

但你可能对如何解决这个问题感兴趣,对吧?查找BigDecimal类.这就是您想要用于计算的内容,尤其是在处理货币时.另外,查找DecimalFormat,它是一个用于将数字写为格式正确的字符串的类.例如,我想当你只想显示2位小数并且你的数字有更多时,它会为你舍入.


Jon*_*eet 9

如果我有一个双打数组,每个都有两个小数位

让我们停在那里,因为我怀疑你没有.例如,您在示例代码中给出9.99.那不是9.99.这是"最接近9.99"的两倍,因为9.99本身不能用二进制浮点精确表示.

那时,你的其他推理就会消失.

如果您想要具有精确十进制数字的值,则应使用以十进制为中心的方式存储值的类型,例如BigDecimal.或者,将所有内容存储为整数并"知道"您实际上正在记住"值*100".

  • +1:浮点表示作为2的一系列幂的数.例如1.625是2 ^ 0 + 2 ^ -1 + 2 ^ -3.所以有四个值,带有两个小数位0.00,0.25,0.50,0.75,这些值不会有舍入(对于+, - ,*和/)或表示错误.所有其他两个小数位值都是近似值. (3认同)