Reputation: 164177
I came across a scenario in which I have two Number
instances and I need to check if one is a multiple of the other.
I tried to use modulo to check that, for example:
public static boolean isMultipleOf(Number a, Number b) {
return a % b == 0;
}
But the compiler doesn't like it:
Operator '%' cannot be applied to 'java.lang.Number', 'java.lang.Number'
I get why this is the case, but my question is what will be the simplest way of achieving this?
The actual values can be any type of number, and I'd like to avoid checking for each of them what is their actual number type and only then perform the operation.
Any ideas?
Thanks!
Upvotes: 2
Views: 104
Reputation: 523344
A Number only provides methods to convert to primitives, and each of the primitive is insufficient to give an accurate answer.
static boolean wrongIsMultipleOfUsingDouble(Number a, Number b) {
return (a.doubleValue() % b.doubleValue()) == 0;
}
Since a double
only has 53 bits of precision, it will give wrong answer when the input is a long
which needs 63 bits of precision:
System.out.println(wrongIsMultipleOfUsingDouble(6969696969696969696L, 3L));
// prints `false`, but should be `true`
System.out.println(wrongIsMultipleOfUsingDouble(7777777777777777777L, 2L));
// prints `true`, but should be `false`.
static boolean wrongIsMultipleOfUsingLong(Number a, Number b) {
return (a.longValue() % b.longValue()) == 0;
}
Obviously it does not work because of truncation.
System.out.println(wrongIsMultipleOfUsingLong(5.0, 2.5));
// prints `false`, but should be `true`
System.out.println(wrongIsMultipleOfUsingLong(4.5, 2.0));
// prints `true`, but should be `false`.
Although OP liked to avoid type checking, this is really the only way to approach an acceptable solution.
static boolean wrongIsMultipleOfUsingTypeChecking(Number a, Number b) {
// pseudo-code for simplicity
if (a, b instanceof (AtomicInteger | AtomicLong | Byte | Integer | Long | ...)) {
return (a.longValue() % b.longValue()) == 0;
} else if (a, b instanceof (Double | DoubleAccumulator | DoubleAdder | Float) {
return (a.doubleValue() % b.doubleValue()) == 0;
} else if (a, b instanceof (BigInteger | BigDecimal)) {
return a.remainder(b) == ZERO;
} else {
throw new RuntimeError("I give up");
}
}
This is fine in most scenario, but again it still does not work because it can't handle third-party subclasses of Number, say, org.apache.commons.math4.fraction.Fraction
?
Now OP stated that Number
is used because the number comes from JSON. Those Number are typically only long
or double
so the type-checking approach is sufficient.
However, most popular libraries in Java also support interpreting numbers as BigDecimal:
.getBigDecimal()
.getAsBigDecimal()
BigDecimal
fieldsjavax.json
(JSR 353) has .bigDecimalValue()
A BigDecimal covers both the range of double
and long
, and has an actual .remainder()
method that solves OP's problem. If we want to perform arithmetic using only a single class, and the price of BigDecimal is not considered a big problem, this can be a viable alternative.
Upvotes: 1
Reputation: 30819
You can use doubleValue
method to convert the arguments to double
and apply the %
, e.g.:
private static boolean isMultipleOf(Number a, Number b){
return (a.doubleValue() % b.doubleValue()) == 0.0;
}
It would work with int
and float
as well, e.g.:
public static void main(String[] args) throws Exception{
System.out.println(isMultipleOf(20, 10));
System.out.println(isMultipleOf(20.0, 10));
System.out.println(isMultipleOf(20, 10.0));
System.out.println(isMultipleOf(20.0, 10.0));
}
The above prints true
4 times.
Update
If you are dealing with huge numbers then you can use BigDecimal
class' remainder
method, e.g.:
private static boolean isMultipleOf(Number a, Number b){
return new BigDecimal(a.doubleValue()).remainder(new BigDecimal(b.doubleValue())).doubleValue() == 0.0;
}
Upvotes: 1
Reputation: 2540
You could do something like this:
public static boolean isMultipleOf(Number a, Number b) {
return a.longValue() % b.longValue() == 0;
}
Of course, this assumes that a
and b
are mathematically integers (so Short, Integer, or Long). You could use doubleValue()
instead, but beware floating point algebra comparisons...
Upvotes: 1