什么时候可以在Java中使用浮点类型进行货币计算?
我了解到我不能使用浮点类型(Java中的浮点类型(什么时候可以在Java中使用浮点类型进行货币计算?,java,math,floating-point,computer-architecture,Java,Math,Floating Point,Computer Architecture,我了解到我不能使用浮点类型(Java中的浮点类型(float/double)进行货币计算(以及需要精确结果时的任何其他计算)。我必须使用decimal数字类型(Java中的BigDecimal) 现在,当我可以使用浮点类型时,我就不知道了。他们是否提供任何精度保证?假设我想用精度0.001计算一些公式。如何知道是否可以使用浮点类型进行此计算?浮点类型取决于硬件,因此没有一般精度保证。当结果不需要“完美”时,您可以使用它们 见: 来自维基百科:() 浮点数约7.2位小数 双精度约15.9分贝 这意
float
/double
)进行货币计算(以及需要精确结果时的任何其他计算)。我必须使用decimal
数字类型(Java中的BigDecimal
)
现在,当我可以使用浮点类型时,我就不知道了。他们是否提供任何精度保证?假设我想用精度
0.001
计算一些公式。如何知道是否可以使用浮点类型进行此计算?浮点类型取决于硬件,因此没有一般精度保证。当结果不需要“完美”时,您可以使用它们
见:
来自维基百科:()
浮点数约7.2位小数
双精度约15.9分贝
这意味着周期在哪里并不重要,一个数字中的精确数字总是有限的
我学会了我不能使用浮点类型(Java中的float/double)来进行货币计算
您不能使用double
来准确表示超过70万亿美元的货币。对于小于此值的值,可以使用double
或long
而不会出现错误(前提是使用适当的舍入)
一旦您了解四舍五入的必要性,使用double
的IMHO就更容易使用
然而,许多人认为它容易出错,因为您不知道谁可能需要维护代码。使用BigDecimal将确保正确使用舍入,并为您提供许多有关其工作方式的选项
许多人感到不安的是,即使0.1
也不能精确表示,而BigDecimal可以显示精确的表示(以及为什么必须小心转换为BigDecimal)
印刷品
0.1000000000000000055511151231257827021181583404541015625
0.30000000000000004
0.30
Cannot represent 7.0368744177664E13 plus 0.01
Can represent 7.0368744177664E13 minus 0.01
许多人使用不当
new BigDecimal(Double.toString(0.1))
或者类似的方法,但具有讽刺意味的是,它的准确度仅与他们试图避免使用的toString方法一样
更有效的方法是
BigDecimal.valueOf(0.1)
这样就不需要创建字符串
支持double
的库将正确显示此数字,但一旦您执行计算,默认轮数可能不够
System.out.println(0.1 * 3);
印刷品
0.1000000000000000055511151231257827021181583404541015625
0.30000000000000004
0.30
Cannot represent 7.0368744177664E13 plus 0.01
Can represent 7.0368744177664E13 minus 0.01
在这种情况下,你必须说出你期望的精度。说你有美元和美分可以用
System.out.printf("%.2f%n", 0.1 * 3); // round output to two decimal places
印刷品
0.1000000000000000055511151231257827021181583404541015625
0.30000000000000004
0.30
Cannot represent 7.0368744177664E13 plus 0.01
Can represent 7.0368744177664E13 minus 0.01
查找无法继续添加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;
}
}
假设我想计算一个精度为0.001的公式。如何知道是否可以使用浮点类型进行此计算
浮点类型是base-2,而不是base-10。1/(10^n)不能用(二进制)浮点数精确表示,就像1/3不能用十进制数精确表示一样。浮点数对于科学类型的计算非常有用,其中的值可能大或小,并且测量的不确定性使计算机的精度限制相形见绌。在支持浮点运算的硬件上,浮点运算速度相当快,而其他非整数类型的浮点运算速度则不那么快
浮点数之所以被称为浮点数,是因为小数点两侧的位数会因数字而异。浮点数基本上表示为
+/-1.a*2^b
,其中a
和b
是以2为基数的数字。这类似于基于十进制的1.a*10^b
。有关浮点的更多信息,请参阅Wikipedia文章。当您可以证明结果适用于您的应用程序时,您可以在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)。另一个例子是,您可以证明几个操作的累积误差小于半美分,因此您可以执行这些操作并将最终结果四舍五入到最接近的美分,这是正确的,因为错误永远不会大到导致最终答案不正确。这段视频刚刚涉及到这个主题:
这是对浮点运算“问题”的一个优秀且易于理解的描述。他们还提到了使用浮点数进行货币计算的问题。您可以使用assert语句通过从精确值中减去计算值来检查精度。但这只会在测试时有所帮助。你只需要学习如何有效地使用浮点数。即使是
0.1
也不能精确地表示为浮点数