20

Possible Duplicate:
Comparing the values of two generic Numbers

I would like to compare two arbitrary instances of Number to find out which is greater. As you know, those may be Integers, Longs or Doubles but also BigIntegers or BigDecimals. So what to do?

  1. Number does not implement Comparable.
  2. Can't use doubleValue() as there might be a loss of precision from BigDecimals or BigIntegers.

Do I really have to go into instance-testing and casting? There must be some library that does this. But I've already checked Apache Commons and Guava with no luck.

Edit 1: Comments pointed this out: Comparing the values of two generic Numbers. It has a solution that converts everything to a BigDecimal using toString(). Is there really no better way to do this?

Edit 2: I just posted my own version as an answer to the other question. It should be more efficient and robust than the accepted answer.

Community
  • 1
  • 1
rolve
  • 9,021
  • 4
  • 50
  • 70
  • 6
    Convert anything into `BigDecimal`? – SJuan76 Sep 24 '12 at 08:41
  • 1
    See this answer and the links in there: http://stackoverflow.com/a/3214469/44522 – MicSim Sep 24 '12 at 08:43
  • 1
    Why would you want to compare arbitrary Number classes? Why are they not all the same type? – Peter Lawrey Sep 24 '12 at 08:43
  • good question, definitely should look at the link provided by @MicSim – UmNyobe Sep 24 '12 at 08:49
  • 1
    I agree with SJuan76. I too ended up with a method that convers any Number to a BigDecimal and then does the comparison. – a_horse_with_no_name Sep 24 '12 at 08:53
  • Thanks for all the comments so far. And thanks for the link to the other question, it didn't show up in the related section. I also thought about converting everything into BigDecimal. The String version is ugly and inefficient but everything else probably needs case distinction again. Maybe something nicer is still discovered... – rolve Sep 24 '12 at 08:58
  • Ok it is a crazy idea but perhaps it will help number1 / number2 = 1 when the numbers are equal number1 / number2 < 1 when number1 is less then number2 number1 / number2 > 1 when number1 is bigger then number2 – JackTools.Net Sep 24 '12 at 09:25
  • @JackTools.Net Just out of intrest, how would you *divide* the numbers generically (not to mention precision issues that may give wrong result)? – Durandal Sep 24 '12 at 12:38
  • Try it with google 10/10 equal or not? 0,12345E5/12345 equal or not? When the google search engine can do that why not java. – JackTools.Net Sep 24 '12 at 18:24
  • @JackTools.Net dividing arbitrary numbers is also a possibly endless operation. Just think of `1/3`. – SpaceTrucker Sep 25 '12 at 14:59
  • That is right the result is 0.3333333333 and so on also less then 1 that means first number is less then second number. I think this is the right result. – JackTools.Net Sep 25 '12 at 18:07

2 Answers2

18

You can't compare two arbitrary instances of type Number, because this is a possibly endless operation. Imagine two irrational numbers, where the only difference is in the billion-th decimal. You would have to compare some billion decimals for this.

You need to convert your Number instances to BigDecimal, applying rounding as necessary, and then compare these both.

SpaceTrucker
  • 11,729
  • 6
  • 48
  • 95
  • Interesting comments, thanks. But since this is for a library, it's not up to me to decide when and where to round. – rolve Sep 24 '12 at 09:12
  • 4
    Then you should give the user of the library the possibility to define what kind of rounding to use and the required precision. – SpaceTrucker Sep 24 '12 at 09:20
7

Strictly speaking, Number can not be compared. You need first to define the semantics of the comparison. Since you mentioned its for library use, you probably want to only rely on what the Number API offers.

So its down to comparing the values of either longValue() or doubleValue(). I suggest an approach that compares using longValue() first, and if they turn out to be equal, compare the doubleValue() as well (this way you catch long overflows, but still maintain 64 bit long precision when the values are not floats):

public static int compare(Number n1, Number n2) {
    long l1 = n1.longValue();
    long l2 = n2.longValue();
    if (l1 != l2)
        return (l1 < l2 ? -1 : 1);
    return Double.compare(n1.doubleValue(), n2.doubleValue());
}

Everything else is a guessing game exceeding what the Number API offers. For standard use above code should work fairly well. And yes, if you pass it (for example) BigIntegers or BigDecimals that are outside of Doubles value range or have a differences only beyond 53 bits of mantissa precision, they are wrongly detected as being equal, but as far as the Number API is concerned this wrong result can be seen as the "correct" one.

Durandal
  • 19,415
  • 2
  • 32
  • 62
  • I think the `double` values should be compared first; otherwise I think a NaN will sort between -1 and the next larger value. – supercat Dec 17 '13 at 19:17