4

I learned I cannot use floating-point types (float/double in Java) to do money calculations (and any other calculations when I need precise results). I have to use decimal number types (BigDecimal in Java) instead.

Now I wounder when I can use the floating-point types. Do they provide any precision guarantee ? Suppose I would like to calculate some formula with precision 0.001. How do I know if I can use floating-point types for this calculation or not?

4

5 回答 5

8

当您可以证明结果对您的应用程序可接受时,您可以在 Java 或其他语言中使用浮点类型。

使用 BigDecimal 本身并不能解决这个问题。例如,假设一个银行账户包含 87.34 美元,并且您必须添加一个月的利息,年利率为 2.37%。首先,BigDecimal 不会正确计算月利率,因为您必须将 2.37% 除以 12,而 2.37/12(或 .0237/12)不能精确地表示为十进制。其次,即使 BigDecimal 确实正确计算了月利率,并且正确计算了 87.34 美元的利息,您可能仍需要将该金额四舍五入到一定数量的美分,然后再将其添加到余额中。该舍入的规则可能在某些法律文件中指定,并且可能与 BigDecimal 进行舍入的方式不匹配。

十进制浮点和二进制浮点都能够精确计算许多结果,比您在示例中建议的 .001 精度要精确得多。当以各种方式使用时,两者都能够产生重大错误。

因此,要使用浮点,您必须了解这些类型可以表示什么值、浮点运算中会发生什么错误、您将执行什么操作以及您的应用程序需要什么。通常,可以通过精心设计操作来避免浮点错误。例如,您可以通过将金额缩放为整数值来处理货币(使用 8734 来表示 87.34 美元,而不是使用 87.34 来表示 87.34)。再举一个例子,你可以证明几个操作的累积误差小于半分,因此你可以执行这些操作并将最终结果四舍五入到最接近的分,这将是正确的,因为误差永远不够大使最终答案不正确。

于 2012-08-08T19:29:51.450 回答
4

我了解到我不能使用浮点类型(Java 中的 float/double)来进行货币计算

你不能用它double来准确地代表超过 70 万亿美元的钱。对于小于此的值,您可以使用doublelong没有错误(前提是您使用适当的舍入)

恕我直言double,一旦您了解了舍入的必要性,使用起来就更简单了。

然而,许多人认为它容易出错,尤其是因为您不知道谁可能需要维护代码。使用 BigDecimal 将确保您正确使用舍入,并为您提供许多关于其工作方式的选项。

许多人发现令人不安的是,即使0.1不是精确表示,BigDecimal 也可以显示精确表示(以及为什么您必须小心如何转换为 BigDecimal)

System.out.println(new BigDecimal(0.1));

印刷

0.1000000000000000055511151231257827021181583404541015625

许多低效使用

new BigDecimal(Double.toString(0.1))

或类似的东西,但具有讽刺意味的是,它只与他们试图避免使用的 toString 方法一样准确。

一种更有效的方法是

BigDecimal.valueOf(0.1)

这避免了创建字符串的需要。

支持的库double将正确显示此数字,但一旦您执行计算,默认轮次可能还不够。

System.out.println(0.1 * 3);

印刷

0.30000000000000004

在这种情况下,您必须说出您期望的精度。假设你有美元和美分可以使用

System.out.printf("%.2f%n", 0.1 * 3); // round output to two decimal places

印刷

0.30

找到你不能再继续添加的地方 0.01

for (double d = 1; d < Long.MAX_VALUE; d *= 2) {
    long l = Double.doubleToLongBits(d);
    double d1 = Double.longBitsToDouble(l + 1);
    if (d1 - d > 0.01) {
        System.out.println("Cannot represent " + d + " plus 0.01");
        double d_1 = Double.longBitsToDouble(l - 1);
        if (d - d_1 < 0.01)
            System.out.println("Can represent " + d + " minus 0.01");
        break;
    }
}

印刷

Cannot represent 7.0368744177664E13 plus 0.01
Can represent 7.0368744177664E13 minus 0.01
于 2012-08-08T18:35:29.440 回答
1

这个视频刚刚浮出水面关于这个话题:

http://www.youtube.com/watch?v=PZRI1IfStY0

这是对浮点运算“问题”的出色且易于理解的描述。他们还提到了使用浮点数进行货币计算的问题。

于 2014-01-24T11:06:00.423 回答
1

假设我想计算一些精度为 0.001 的公式。我如何知道我是否可以使用浮点类型进行此计算?

浮点类型是 base-2,而不是 base-10。1/(10^n) 不能用(二进制)浮点数精确表示,就像 1/3 不能用十进制数精确表示一样。浮点数对于科学类型的计算很有用,其中值可能很大或很小,并且测量的不确定性使计算机的精度限制相形见绌。浮点运算在支持它的硬件上非常快,而其他非整数类型则没有那么快。

浮点数之所以被称为浮点数,是因为小数点两边的位数会因数字而异。浮点数本质上表示为+/- 1.a * 2^bwherea并且b是以 2 为底的数字。这类似于基于十进制的科学记 1.a * 10^b数法。有关IEEE 754浮点的更多信息,请参阅 Wikipedia 文章。

于 2012-08-08T19:01:28.840 回答
-1

浮点类型取决于硬件,因此没有一般的精度保证。当结果不需要“完美”时,您可以使用它们。

见:http ://en.wikipedia.org/wiki/Pentium_FDIV_bug

来自维基百科:(http://en.wikipedia.org/wiki/Floating_point

~7.2 小数浮点数

~15.9 十进制双倍

这意味着句点在哪里并不重要,数字中的精确数字总是有限的。

于 2012-08-08T18:15:58.707 回答