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/ylog(y)这样的表达式,在实际意义上,y是否为零(在这种情况下,您得到奇点误差)或y是非常接近零的数字并不重要(在这种情况下,您将得到一个非常大的数字,其数量对y的值非常敏感 - 从数值的角度来看,这两种情况都是“坏的”,我需要重新评估我正在尝试做什么,以及我的行为是什么当y值在零附近时,我正在寻找。

根据someAmount的计算方式,您可能会发现浮点/双精度的一些奇怪行为

基本上,使用float / double将数值数据转换为二进制表示是容易出错的,因为有些数字不能用螳螂/指数表示。

有关此的一些详细信息,您可以阅读这篇小文章

你应该考虑使用java.lang.Math.signumjava.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) 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.我认为这种方法是我遇到的最好的方法。

如何测试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。

玩的开心 @。@