开发者

Comparing Numbers in Java

In Java, all numeric types extend from java.lang.Number. Would it be a good idea to have a method like the following:

public boolean areEqual(Number first, Number second) {
    if (first != null && second != null) {
        return first.equals(second);
    }
}

I'm concerned about cases where a double 2.00000 does not equal an int 2. Are these h开发者_运维技巧andled by the built-in equals? If not, is there any way to write a simple number compare function in java? (external libraries such as apache commons are ok)


A Double is NEVER equals to an Integer. Moreover, a double is not the same as a Double.

Java has primitive types and reference types. The truly numeric types in Java do not extend from Number, because they're primitives.

You may want to consider a system where you're not mixing types, because that usually will cause a lot of trouble with implicit/explicit conversions that may/may not lose information, etc.

Related questions

On int vs Integer:

  • What is the difference between an int and an Integer in Java/C#?
  • Is Java fully object-oriented?

On Number comparison:

  • Why doesn't java.lang.Number implement Comparable?
  • Comparing the values of two generic Numbers

See also

  • Java Language Guide/Autoboxing
  • JLS 4.2 4.2 Primitive Types and Values

    The numeric types are the integral types and the floating-point types. The integral types are byte, short, int, and long and char. The floating-point types are float and double.


On mixed-type computation

Mixed-type computation is the subject of at least 4 puzzles in Java Puzzlers.

Here are various excerpts:

it is generally best to avoid mixed-type computations [...] because they are inherently confusing [...] Nowhere is this more apparent than in conditional expressions. Mixed-type comparisons are always confusing because the system is forced to promote one operand to match the type of the other. The conversion is invisible and may not yield the results that you expect

Prescription: Avoid computations that mix integral and floating-point types. Prefer integral arithmetic to floating-point.


To compare two Numbers in Java you can use the compareTo from BigDecimal. BigDecimal can hold everything from short until double or BigInteger, so it's the perfect class for this.

So you can try to write something like this:

public int compareTo(Number n1, Number n2) {
    // ignoring null handling
    BigDecimal b1 = BigDecimal.valueOf(n1.doubleValue());
    BigDecimal b2 = BigDecimal.valueOf(n2.doubleValue());
    return b1.compareTo(b2);
}

This is surely not the best approach regarding to performance. The following tests worked so far, at least with JDK7:

assertTrue(compareTo(new Integer(1), new Integer(2)) == -1);
assertTrue(compareTo(new Integer(1), new Double(2.0)) == -1);
assertTrue(compareTo(new Integer(1), new Double(Double.MAX_VALUE)) == -1);
assertTrue(compareTo(new Integer(1), new Double(Double.MIN_VALUE)) == 1);
assertTrue(compareTo(new Integer(1), new Double(1.000001)) == -1);
assertTrue(compareTo(new Integer(1), new Double(1.000)) == 0);
assertTrue(compareTo(new Integer(1), new Double(0.25*4)) == 0);
assertTrue(compareTo(new Integer(1), new AtomicLong(1)) == 0);


The specific method you suggest would fail, because it's using equals() inherited from Object. That is, it would check to see if the Number objects were the same, not whether their values were the same.

If that was just an illustrative example, I will update my answer.

polygene's answer actually pretty much covers the ground I was heading for. You may also be interested in this question: Why doesn't java.lang.Number implement Comparable?.


If you want to know whether the object references are the same, then the existing methods fit the bill. A Double representing 2.0 and an Integer representing 2 are definitely different objects, and certainly not interchangeable in a general sense.

If you just want to know whether the numeric values are the same, you can use the Number.doubleValue() method to convert both numbers to doubles, then compare those numbers together (probably allowing for a small tolerance, as most numbers are represented inexactly, such as 1.99999999996583 for what should be 2, depending on the intermediate calculation steps). Something like the following:

private static final double EPSILON = 0.000000000000001d;    

public static boolean areEquivalentNumbers(Number a, Number b)
{
   if (a == null)
   {
      return b == null;
   }
   else if (b == null)
   {
      return false;
   }
   else
   {
      return Math.abs(a.doubleValue() - b.doubleValue()) < EPSILON;
   }
}


On a tangent to a couple of the responses, may I suggest that instead of writing something like:

boolean compare(Object o1, Object o2)
{
  if (o1==null)
    return o2==null;
  if (o2==null)
    return false;
  return o1.equals(o2);
}

It's much more concise, and I believe slightly more efficient, to write:

boolean compare(Object o1, Object o2)
{
  return o1==o2 || o1!=null && o2!=null && o1.equals(o2);
}

If both are null, o1==o2 will return true. If they're not but they're the same object, that's fine too.

Technically the o2!=null is not necessary for most implementations of equals, but if you were really being so generic as to do this on Objects as in the above example, you of course wouldn't know how every override was written.


    public static boolean compareTo(Number d1, Number d2) {
    Double num1=d1.doubleValue();
    Double num2=d2.doubleValue();
     if(Double.compare(num1, num2)==0)
         return true;
     else
         return false;

}

OR
    public static boolean compareTo(Number d1, Number d2) {
     if(d1.doubleValue()==d2.doubleValue())
         return true;
     else
         return false;

}


Comparing numbers between integer and floating point is almost never going to yield what you are after. If however this is a simple exercise, you could implement the comparison by comparing the string representations of the values, as in:

public boolean areEqual(Number first, Number second) {
    if (first == null) {
        return second == null;
    }
    if (second == null) {
        return false;
    }

    return first.toString().equals(second.toString());
}


you cannot call

number.equals(number2);

because, if number is a Double and number2 is an Integer, they will not be of the same class and you will get an exception telling you of that fact.

You could write a comparison class yourself that accepts Number objects, but you will have to take into account the different subclasses of Number

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜