如何对两个 Number 实例使用模数?

How to use modulo with two instances of Number?

我遇到了一个场景,我有两个 Number 个实例,我需要检查一个是否是另一个的倍数。
我尝试使用模数来检查,例如:

public static boolean isMultipleOf(Number a, Number b) {
    return a % b == 0;
}

但是编译器不喜欢它:

Operator '%' cannot be applied to 'java.lang.Number', 'java.lang.Number'

我明白为什么会这样,但我的问题是实现这一目标的最简单方法是什么?
实际值可以是任何类型的数字,我想避免检查他们每个人的实际数字类型是什么,然后才执行操作。

有什么想法吗?
谢谢!

你可以这样做:

public static boolean isMultipleOf(Number a, Number b) {
    return a.longValue() % b.longValue() == 0;
}

当然,这假设 ab 在数学上是整数(因此是 Short、Integer 或 Long)。您可以改用 doubleValue(),但要注意浮点代数比较...

您可以使用 doubleValue 方法将参数转换为 double 并应用 %,例如:

private static boolean isMultipleOf(Number a, Number b){
    return (a.doubleValue() % b.doubleValue()) == 0.0;
}

它也适用于 intfloat,例如:

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));
}

以上打印true 4次。

更新

如果你正在处理大量的数字,那么你可以使用 BigDecimal class' remainder 方法,例如:

private static boolean isMultipleOf(Number a, Number b){
    return new BigDecimal(a.doubleValue()).remainder(new BigDecimal(b.doubleValue())).doubleValue() == 0.0;
}

不,如果不进行类型检查,你就做不到

一个Number只提供了转换为图元的方法,每个图元都不足以给出准确的答案。

doubleValue 不起作用

static boolean wrongIsMultipleOfUsingDouble(Number a, Number b) {
    return (a.doubleValue() % b.doubleValue()) == 0;
}

由于double只有53位精度,当输入long需要63位精度时会给出错误答案:

System.out.println(wrongIsMultipleOfUsingDouble(6969696969696969696L, 3L));
// prints `false`, but should be `true`
System.out.println(wrongIsMultipleOfUsingDouble(7777777777777777777L, 2L));
// prints `true`, but should be `false`.

longValue 不起作用

static boolean wrongIsMultipleOfUsingLong(Number a, Number b) {
    return (a.longValue() % b.longValue()) == 0;
}

显然它因为截断而不起作用。

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

类型检查仅适用于已知类型。

尽管 OP 喜欢避免类型检查,但这确实是接近可接受解决方案的唯一方法。

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");
    }
}

这在大多数情况下都很好,但它仍然无法正常工作,因为它无法处理 Number 的第三方子 classes,例如 org.apache.commons.math4.fraction.Fraction?

仅限 JSON 个号码?

现在 OP 声明使用 Number 因为数字来自 JSON。这些数字通常只有 longdouble,因此类型检查方法就足够了。

但是,most popular libraries in Java 也支持将数字解释为 BigDecimal:

BigDecimal 涵盖了 doublelong 的范围,并且有一个实际的 .remainder() method 解决了 OP 的问题。如果我们只想使用单个 class 来执行算术运算,并且 BigDecimal 的价格被认为不是一个大问题,这可能是一个可行的选择。