【问题标题】:How to use modulo with two instances of Number?如何对两个 Number 实例使用模数?
【发布时间】:2017-08-28 19:13:16
【问题描述】:

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

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

但是编译器不喜欢它:

运算符“%”不能应用于“java.lang.Number”, 'java.lang.Number'

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

有什么想法吗?
谢谢!

【问题讨论】:

  • 不能,不能,不能这样做。 放弃试图概括不同类型的Number;它的设计真的不是为了能够做这样的事情。每个人都试图这样做,但每个人都失败了。甚至不要尝试。您可以使用Number 做的唯一一件事就是尽可能将其转换为原始数字类型,即使这样您也不知道您是否会降低精度。
  • @LouisWasserman 我只使用Number,因为我希望能够拥有null,而原语不允许这样做。显然我需要转换为原语(否则编译器不会让我这样做),问题是“最简单的方法是什么”,似乎使用doubleValue 是。
  • 不一定会产生有效的结果。 long 值在转换为 double 时会四舍五入,这会弄乱 % 结果。你为什么想要空值?空数字没有意义。
  • 我建议重新设计您的整个程序,以便您坚持使用一种类型来表示您的数字。
  • 所以?将它们都转换为 BigDecimal,并普遍使用。

标签: java numbers modulo


【解决方案1】:

你可以这样做:

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

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

【讨论】:

    【解决方案2】:

    您可以使用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;
    }
    

    【讨论】:

    • @kennytm 添加了应该与 BigDecimal 一起使用的版本。
    • 谢谢,我会接受的。我不会使用大量数字,所以你的第一个选择就可以了。
    • @DarshanMehta 不,我说的是数字 ab 已经是 BigDecimala = new BigDecimal("3E+1000")b = new BigDecimal("2E+1000")
    【解决方案3】:

    不,不检查类型是不行的

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

    doubleValue 不起作用

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

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

    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 的第三方子类,例如 org.apache.commons.math4.fraction.Fraction?

    仅限 JSON 数字?

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

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

    BigDecimal 涵盖了doublelong 的范围,并且有一个实际的.remainder() method 可以解决OP 的问题。如果我们只想使用单个类来执行算术,并且 BigDecimal 的价格不是一个大问题,那么这可能是一个可行的替代方案。

    【讨论】:

    • 感谢您的深刻回答。就我的目的而言,使用BigDecimal 可能有点矫枉过正,因为我们将使用的数字只会是双倍且长。但它具有教育意义!
    猜你喜欢
    • 1970-01-01
    • 2017-11-27
    • 2017-01-29
    • 1970-01-01
    • 2016-08-22
    • 2018-10-15
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    相关资源
    最近更新 更多