将货币存储在 Java double(浮点数)中,无需任何数学运算,总是准确的吗?

Is storing currency in Java double (floating point), without any math, always accurate?

当然不应该做数学运算,因为结果不会准确。浮点值不适用于此。

但是仅存储值呢?就个人而言,我会选择 String 或 Long,但看起来我有时可能被迫与坚持浮点类型的系统进行交互。

看起来 从 0.00 到 2.00 的值是 100% 准确的 - 请参阅下面的代码。但真的是这样吗? 为什么? 我只是做 double v = 0.01 时不应该已经有问题了吗?

public static void main(final String[] args) {

    final DecimalFormat df = new DecimalFormat("0.0000000000000000000000000", DecimalFormatSymbols.getInstance(Locale.US));
    final BigDecimal aHundred = new BigDecimal("100");
    final BigDecimal oneHundredth = BigDecimal.ONE.divide(aHundred);
    for (int i = 0; i < 200; i++) {
        BigDecimal dec = oneHundredth;

        for (int ii = 0; ii < i; ii++) {
            dec = dec.add(oneHundredth);
        }

        final double v = dec.doubleValue();

        System.err.println(v);
        System.err.println(df.format(v));
    }
    System.exit(0);
}

输出:

0.01
0.0100000000000000000000000
0.02
0.0200000000000000000000000
0.03
0.0300000000000000000000000
...
1.38
1.3800000000000000000000000
1.39
1.3900000000000000000000000
1.4
1.4000000000000000000000000
1.41
1.4100000000000000000000000
...
1.99
1.9900000000000000000000000
2.0
2.0000000000000000000000000

Is storing currency in Java double (floating point), without any math, always accurate?

如果您将货币值表示为最小货币单位(例如美分)的倍数,那么您实际上可以使用 53 位精度...计算结果为 9.0 x 1015 美分,或 9.0 x 1013 美元。

(就规模而言,美国国债目前约为 2.8 x 13 美元。)

并且如果您尝试以(例如)浮点美元(使用 double)表示货币值,那么大多数美分值根本无法精确表示。只有 25 美分的倍数在二进制浮点数中具有精确表示。

简而言之,即使您不对值执行算术运算,它也可能不精确。

从十进制转换为基于二进制的浮点数,反之亦然是数学。这是一种将结果舍入到最接近的可表示值的操作。

当您将 .01 转换为 double 时,结果正好是 0.01000000000000000020816681711721685132943093776702880859375。 Java默认的显示格式可能显示为“0.01”,但实际值为 0.01000000000000000020816681711721685132943093776702880859375。

Java 的 double 格式的精度是这样的,如果任何具有最多 15 个有效小数位的十进制数字四舍五入到最接近的可表示 double,然后 double四舍五入到最接近的15位有效数字或更少的小数,结果将是原来的数字。

因此,您可以使用 double 来存储任何最多 15 位有效数字(在指数范围内)的十进制数字,并且可以通过将其转换回十进制来恢复原始数字。超过 15 位数字,一些数字将被往返更改。