24

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 handled 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)

David
  • 3,215
  • 7
  • 23
  • 24

8 Answers8

45

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:

On Number comparison:

See also


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.

Community
  • 1
  • 1
polygenelubricants
  • 348,637
  • 121
  • 546
  • 611
13

I know its an old topic, but.... To compare two Numbers in Java you can use the compareTo method from BigDecimal. BigDecimal can hold everything from short until double or BigInteger, so its 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 = new BigDecimal(n1.doubleValue());
    BigDecimal b2 = new BigDecimal(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);
Lian
  • 257
  • 3
  • 3
  • 3
    For all `Number` implementations, prefer the `Type.valueOf(value)` to `new Type(value)`. They all provide caches, which will save memory in large scale applications. – pickypg May 10 '14 at 16:17
  • 1
    I've just downvoted your answer because the conversion of Long.MAX_VALUE to a BigDecimal by using doubleValue() gives 9223372036854775808 instead of 9223372036854775807. It is even worse with the conversion from BigInteger to BigDecimal. Please follow pickypg's advise for correctness' sake, rather call BigDecimal.valueOf(long) and new BigDecimal(BigInteger) when converting integral numbers. 1647684687678982643434436666777443865 becomes 1647684687678982604257012050286346240 when applying your method on a number storable into a BigInteger. – gouessej Apr 13 '18 at 13:43
6

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?.

Community
  • 1
  • 1
Pops
  • 28,257
  • 34
  • 127
  • 149
  • 2
    @polygenelubricants If I had to guess it's that the description of `equals` is inaccurate for all built-in Java implementations of `Number`. They all override `equals` to do an `instanceOf` check (and therefore a `null` check) and then check the raw value rather than doing a reference check, which is what is described. In other words, they _do_ check their `Class` _and_ value. – pickypg May 10 '14 at 16:25
  • Correct. Any Java object whose equals() method does reference comparison is in violation of the equals() contract. You are always supposed to override equals and implement it correctly. People just don't because they're lazy. – SigmaX Mar 17 '15 at 21:06
4

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;
   }
}
Andrzej Doyle
  • 97,637
  • 30
  • 185
  • 225
  • 1
    Quibble: any correctly implemented equals() method compares data, not references. – SigmaX Mar 17 '15 at 21:11
  • Since I often don't even know whether I'm getting strings, integers, or decimals, Andrzej's answer is *great*. In fact, I also have: public static boolean areEquivalentNumbers(String a, String b) { return areEquivalentNumbers(Double.parseDouble(a), Double.parseDouble(b)); } – Tihamer Feb 14 '19 at 02:48
2

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.

Jay
  • 25,388
  • 9
  • 54
  • 105
1
    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;

}
0

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());
}
rsp
  • 22,242
  • 6
  • 51
  • 64
-1

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

Richard
  • 9,114
  • 4
  • 24
  • 31