将双精度值与零进行比较时,我们需要小心吗?
if ( someAmount <= 0){
.....
}
如果你想非常小心,你可以测试它是否在零的某个 epsilon 之内,比如
double epsilon = 0.0000001;
if ( f <= ( 0 - epsilon ) ) { .. }
else if ( f >= ( 0 + epsilon ) ) { .. }
else { /* f "equals" zero */ }
或者你可以简单地将你的双打四舍五入到某个指定的精度,然后再对它们进行分支。
有关比较浮点数误差的一些有趣细节,请参阅Bruce Dawson 的一篇文章。
对于平等:(即==
或!=
)是。
对于其他比较运算符(<
, >
, <=
, >=
),这取决于您是否在边缘情况下,例如是否<
等价于<=
,这是另一种相等的情况。如果您不关心边缘情况,这通常无关紧要,尽管这取决于您的输入数字来自何处以及如何使用它们。
如果您期望(3.0/10.0) <= 0.3
评估为true
(如果浮点错误导致 3.0/10.0 评估为略大于 0.3 的数字,例如 0.300000000001,则可能不会),并且如果评估为false
- 这是一个边缘情况,您的程序将表现不佳,并且你需要小心。
好的数值算法几乎不应该依赖于等式和边缘情况。如果我有一个算法将 ' ' 作为输入,x
它是 0 到 1 之间的任意数字,一般来说,0 < x < 1
或0 <= x <= 1
. 但也有例外:在评估具有分支点或奇点的函数时必须小心。
如果我有一个中间量y
并且我期待y >= 0
并且我评估sqrt(y)
,那么我必须确定浮点错误不会导致 y 是一个非常小的负数并且sqrt()
函数会引发错误。(假设这是不涉及复数的情况。)如果我不确定数字错误,我可能会sqrt(max(y,0))
改为评估。
对于1/y
or之类的表达式log(y)
,在实际意义上,y 是否为零(在这种情况下会出现奇点错误)或 y 是非常接近于零的数字(在这种情况下会得到一个非常大的数字)并不重要,其大小对 y 的值非常敏感)——从数值的角度来看,这两种情况都是“坏的”,我需要重新评估我正在尝试做什么,以及我在寻找什么行为时值y
是在零附近。
根据您someAmount
的计算方式,您可能会期望浮点/双精度出现一些奇怪的行为
基本上,使用浮点数/双精度数将数字数据转换为二进制表示容易出错,因为有些数字不能用螳螂/指数表示。
有关这方面的一些详细信息,您可以阅读这篇小文章
您应该考虑使用java.lang.Math.signum
or java.math.BigDecimal
,尤其是用于货币和税收计算
注意自动拆箱:
Double someAmount = null;
if ( someAmount <= 0){
繁荣,NullPointerException。
是的,你应该小心。
建议:一种好方法是BigDecimal
用于将相等/不相等检查为 0:
BigDecimal balance = pojo.getBalance();//get your BigDecimal obj
0 != balance.compareTo(BigDecimal.ZERO)
解释 :
该compareTo()
函数将 thisBigDecimal
与指定的BigDecimal
. 此方法认为两个BigDecimal
值相等但比例不同的对象(如 2.0 和 2.00)是相等的。此方法优先于六个boolean
比较运算符中的每一个的单独方法提供(<, ==, >, >=, !=, <=)
。执行这些比较的建议习惯用法是:(x.compareTo(y) <op> 0)
,其中 是六个比较运算符之一。
(感谢 SonarQube 文档)
浮点数学是不精确的,因为将这些值存储在二进制表示中存在挑战。更糟糕的是,浮点数学不是关联的;通过一系列简单的数学运算推动浮点数或双精度数,由于每一步都会发生舍入,答案将根据这些运算的顺序而有所不同。
即使是简单的浮点赋值也不简单:
float f = 0.1; // 0.100000001490116119384765625
double d = 0.1; // 0.1000000000000000055511151231257827021181583404541015625
(结果会因编译器和编译器设置而异);
因此,在 float 或 double 值上使用等式 (==) 和不等式 (!=) 运算符几乎总是错误的。相反,最好的做法是完全避免浮点比较。如果不可能,您应该考虑使用 Java 的浮点处理数字之一,例如 BigDecimal,它可以正确处理浮点比较。第三种选择不是寻找相等性,而是寻找值是否足够接近。即将存储值和预期值之间的差值的绝对值与可接受的误差范围进行比较。请注意,这并不涵盖所有情况(例如 NaN 和 Infinity)。
如果您不关心边缘情况,那么只需测试someAmount <= 0
. 它使代码的意图清晰。如果您确实关心,那么...这取决于您如何计算someAmount
以及为什么要测试不等式。
检查 double 或 float 值是否为 0,错误阈值用于检测该值是否接近 0,但不完全为 0。我认为这种方法是我遇到的最好的方法。
如何测试双精度数是否为零? 由@William Morrison 回答
public boolean isZero(double value, double threshold){
return value >= -threshold && value <= threshold;
}
例如,将阈值设置为 0。像这样,
System.out.println(isZero(0.00, 0));
System.out.println(isZero(0, 0));
System.out.println(isZero(0.00001, 0));
上述示例代码的结果为真、真和假。
玩得开心 @。@