如何测试一个双精度浮点数是否为零?

64

我有一些类似这样的代码:

class Foo {
    public double x;
}

void test() {
    Foo foo = new Foo();

    // Is this a valid way to test for zero? 'x' hasn't been set to anything yet.
    if (foo.x == 0) {

    }

    foo.x = 0.0;

    // Will the same test be valid?
    if (foo.x == 0) {

    }
}

我基本上想在未来避免除以零的异常。

谢谢


20
为什么不试试呢? - nanofarad
1
数值数据类型的默认值为0,所以是的。 - Josh M
3
存在正零和负零,x == 0测试两者都适用,并且永远不会给您异常或错误。只有在您将两个整数相除并且分母为0时,才会出现除以零的错误。由于您正在使用浮点数,因此永远不会出现异常。 - Peter Lawrey
"'x' 还没有被设置为任何值。" 实际上已经设置了。它是一个成员变量,因此默认设置为零。 - user207421
6个回答

69

如果在类范围内,数值原语未显式初始化,则其将被初始化为零。

在局部范围内(即方法中的变量),数值原语必须显式初始化。

如果只担心除零异常,检查您的double类型数字是否确切地为零就足够了。

if(value != 0)
    //divide by value is safe when value is not exactly zero.

否则,当检查像doublefloat这样的浮点值是否为0时,会使用一个误差阈值来检测该值是否接近于0,但不是完全等于0。

public boolean isZero(double value, double threshold){
    return value >= -threshold && value <= threshold;
}

6
只有作用域为类的数值原始类型才会被初始化为零,加一赞同让这一点清晰明了。 - anubhava
5
如何定义一个合适的错误阈值?这是很主观的,可能并不能实现用户所需求的结果。如果阈值设置得太大,你可能会将非零值误判为零值。 - Anshul
@Anshul 哎呀,感谢您的批评!在重新阅读问题后,我已经编辑了我的答案。错过了他担心除以0的问题。 - William Morrison
"if(value != 0)" 不起作用,我有 value = 0.0,但这个 if 语句通过了,导致我得到了除以零的错误。 - Max Husiv
1
请注意,0.0 == 0.0-0.0 == 0.0 都返回 true,而 Double.compare(0.0, 0.0) 返回 0,而 Double.compare(-0.0, 0.0) 返回 -1。请谨慎选择您的策略。 - Hummeling Engineering BV
显示剩余4条评论

12

是的,所有基本数字类型的默认值都为0

但是,涉及浮点类型(doublefloat)的计算可能不精确,因此通常最好检查它是否接近0

if (Math.abs(foo.x) < 2 * Double.MIN_VALUE)

你需要选择一个容忍度(margin of error),这并不简单。


不是真的只有原始类型默认为Double(而这是双倍),才会为空。 - Jackie
@Jackie Double 不是数值类型。 - arshajii
什么是数字类型?你是想说原始类型吗? - Jackie
好的,但是没有"numeric"类型,只有基本类型。这些基本类型可以表示数值。http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html - Jackie
很好的发现,理解“==”不是处理浮点数的正确方法。 - Roee Gavirel
显示剩余4条评论

9

在Java中,0和0.0是相同的,而double类型默认为0(尽管许多人建议始终显式设置它们以提高可读性)。我已经确认,如果foo.x为零,则foo.x == 0foo.x == 0.0都为真。


3
通常情况下,明确将变量设置为默认值并不能提高可读性,反而会降低可读性和使代码变得混乱。这是多余的操作,不必要的。 - Michael
@PenguinAgen 更明确地表达 0.0d。 - Gaurav

3

是的,尽管从int到double存在隐式转换,但这是一个有效的测试。为了清晰和简单起见,您应该使用(foo.x == 0.0)进行测试。这将阻止NAN错误/除以零,但双精度值在某些情况下可能非常非常接近于0,但不完全等于0,然后测试将失败(现在我谈论的是一般情况,而不是您的代码)。对此进行除法运算会得到巨大的数字。

如果与货币有关,请勿使用float或double,而应改用BigDecimal。


0
  // The float data type is a single-precision 32-bit IEEE 754 floating-point number, which has a precision of approximately 6-7 decimal digits.
  public static boolean isZero(float val) {
    return Math.abs(val) < 1e-6;
  }

  // The double data type is a double-precision 64-bit IEEE 754 floating-point number, which has a precision of approximately 15-16 decimal digits.
  public static boolean isZero(double val) {
    return Math.abs(val) < 1e-15;
  }

1
所以你基本上明确提供了一个“阈值”,正如其他答案中所指出的。也许你可以[编辑]你的回答,并解释为什么你选择的这个阈值是一个好的选择? - Abra
似乎是一个很好的建议 - Kaibo

-3
最安全的方法是将您的双精度数与0进行按位或运算。 看看这个在Java中对两个双精度数进行异或

基本上,您应该执行if ((Double.doubleToRawLongBits(foo.x) | 0 ) )(如果它确实为0)


13
如果一个Java开发者用按位或测试零,我会感到担忧。 - Kayaman
1
嗯,看起来我做错了。上面的代码将忽略负零。使用 == 0.0 应该可以正常工作。 - Anshul
3
@Ansul: 此外,双精度浮点数有两个零:0.0-0.0(因为它使用带符号的方法)。换句话说,它可能返回10000...。但是,当涉及浮点数时,通常检查相等性是不好的想法。 - Willem Van Onsem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接