Java:双值比较
将双精度值与零进行比较时,我们需要小心吗?
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
或log(y)
这样的表达式,在实际意义上,y是否为零(在这种情况下,您得到奇点误差)或y是非常接近零的数字并不重要(在这种情况下,您将得到一个非常大的数字,其数量对y的值非常敏感 - 从数值的角度来看,这两种情况都是“坏的”,我需要重新评估我正在尝试做什么,以及我的行为是什么当y
值在零附近时,我正在寻找。
根据someAmount
的计算方式,您可能会发现浮点/双精度的一些奇怪行为
基本上,使用float / double将数值数据转换为二进制表示是容易出错的,因为有些数字不能用螳螂/指数表示。
有关此的一些详细信息,您可以阅读这篇小文章
你应该考虑使用java.lang.Math.signum
或java.math.BigDecimal
,特别是对于货币和税收计算
注意自动拆箱:
Double someAmount = null; if ( someAmount <= 0){
Boom,NullPointerException。
是的,你应该小心。
建议:使用BigDecimal
检查等于/不等于0的好方法之一是:
BigDecimal balance = pojo.getBalance();//get your BigDecimal obj 0 != balance.compareTo(BigDecimal.ZERO)
说明:
compareTo()
函数将此BigDecimal
与指定的BigDecimal
进行比较。 这个方法认为两个值相等但具有不同比例(如2.0和2.00)的BigDecimal
对象是相同的。 对于六个boolean
比较运算符(<, ==, >, >=, !=, <=)
每一个,优先考虑该方法。 建议执行这些比较的习惯用法是: (x.compareTo(y)
,其中六个比较运算符之一。
(感谢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.我认为这种方法是我遇到的最好的方法。
如何测试double是否为零? 由@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));
以上示例代码的结果为true,true和false。
玩的开心 @。@