I want to compare to variables, both of type T extends Number
. Now I want to know which of the two variables is greater than the other or equal. Unfortunately I
System.out.println(new BigDecimal(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1f).toPlainString());
System.out.println(Float.valueOf(0.1f).toString());
System.out.println(Float.valueOf(0.1f).doubleValue());
One solution that might work for you is to work not with T extends Number
but with T extends Number & Comparable
. This type means: "T
can only be set to types that implements both the interfaces."
That allows you to write code that works with all comparable numbers. Statically typed and elegant.
This is the same solution that BennyBoy proposes, but it works with all kinds of methods, not only with comparator classes.
public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) {
if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger");
}
public void test() {
compfunc(2, 1); // Works with Integer.
compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable.
compfunc(2, 1.0); // Compilation error! Different types.
compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable
}
The most "generic" Java primitive number is double, so using simply
a.doubleValue() > b.doubleValue()
should be enough in most cases, but... there are subtle issues here when converting numbers to double. For example the following is possible with BigInteger:
BigInteger a = new BigInteger("9999999999999992");
BigInteger b = new BigInteger("9999999999999991");
System.out.println(a.doubleValue() > b.doubleValue());
System.out.println(a.doubleValue() == b.doubleValue());
results in:
false
true
Although I expect this to be very extreme case this is possible. And no - there is no generic 100% accurate way. Number interface have no method like exactValue() converting to some type able to represent number in perfect way without loosing any information.
Actually having such perfect numbers is impossible in general - for example representing number Pi is impossible using any arithmetic using finite space.
After having asked a similar question and studying the answers here, I came up with the following. I think it is more efficient and more robust than the solution given by gustafc:
public int compare(Number x, Number y) {
if(isSpecial(x) || isSpecial(y))
return Double.compare(x.doubleValue(), y.doubleValue());
else
return toBigDecimal(x).compareTo(toBigDecimal(y));
}
private static boolean isSpecial(Number x) {
boolean specialDouble = x instanceof Double
&& (Double.isNaN((Double) x) || Double.isInfinite((Double) x));
boolean specialFloat = x instanceof Float
&& (Float.isNaN((Float) x) || Float.isInfinite((Float) x));
return specialDouble || specialFloat;
}
private static BigDecimal toBigDecimal(Number number) {
if(number instanceof BigDecimal)
return (BigDecimal) number;
if(number instanceof BigInteger)
return new BigDecimal((BigInteger) number);
if(number instanceof Byte || number instanceof Short
|| number instanceof Integer || number instanceof Long)
return new BigDecimal(number.longValue());
if(number instanceof Float || number instanceof Double)
return new BigDecimal(number.doubleValue());
try {
return new BigDecimal(number.toString());
} catch(final NumberFormatException e) {
throw new RuntimeException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e);
}
}
This should work for all classes that extend Number, and are Comparable to themselves.
class NumberComparator<T extends Number> implements Comparator<T> {
public int compare(T a, T b){
if (a instanceof Comparable)
if (a.getClass().equals(b.getClass()))
return ((Comparable<T>)a).compareTo(b);
throw new UnsupportedOperationException();
}
}
if(yourNumber instanceof Double) {
boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
// [...]
}
Note: The instanceof
check isn't necessarily needed - depends on how exactly you want to compare them. You could of course simply always use .doubleValue()
, as every Number should provide the methods listed here.
Edit: As stated in the comments, you will (always) have to check for BigDecimal and friends. But they provide a .compareTo()
method:
if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) {
boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
}