Java MOD 运算符 returns 负值

Java MOD operator returns negative value

我有这个方法:

private static int generateNo(int randomNo, int value){
    return   ((randomNo*value)%256);
}

在我的例子中 随机数 = 17719 qValue = 197920

当我用计算器计算时,返回值应该是 224,然而,当我 运行 程序时,它 returns -32.

谁能解释一下。

17719*197920 = 3506944480,大于Integer.MAX_VALUE.

这样,乘法溢出了int的范围,结果是-788022816

因此,取模会导致负结果。

一点提示。如果在乘法(或求和)数字时出现意外的负值,这主要是数字溢出:

private static int generateNo(int randomNo, int value) {
    return (int)(((long)randomNo * value) % 256);
}

这里有两件事在起作用。

  1. 将两个 int 相乘可以得到大于 Integer.MAX_VALUE (2147483647) 的数字,它回绕为负数。
  2. 对正数和负数应用取模运算符得到负数。

您需要考虑您希望此函数如何在给定边缘情况值(例如非常大的整数或负数)的情况下工作。

例如generateNo(-100, 50)应该生产什么?

您可能希望在执行模数之前确保您的值是正数,如下所示:

Math.abs(randomNo * value) % 256

然而,这实际上有一个非常有趣的边缘情况,其中 Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE 因为它溢出了。

相反,在 结果上使用 Math.abs:

Math.abs((randomNo * value) % 256)

我还将提供一些关于此功能的一般性评论。这些名称并不能真正解释它的作用。为什么 generateNo?毫无疑问,有很多方法可以生成数字。我会建议一个更具体的名称。

参数,randomNovalue也有问题。为什么 generateNo 关心第一个参数是否随机?

更清楚地指定您想要发生的事情,并使用描述这些事情的名称,可能会更容易思考。

我还建议,当遇到此类问题时,分解步骤以便您了解发生了什么。类似于:

private static int generateNo(int randomNo, int value){
    final int product = randomNo * value;
    final int result = product % 256;

    // Breakpoint or System.out.println here, to understand the values...
    return result;
}

Java 属于使用 有符号余数 的阵营,而不是通常表示模数(欧几里德除法的非负余数)的运算。幸运的是,对于 2 的幂,有一个非常简单的解决方法:使用按位 &。无论如何,这更容易考虑,因为它是对位的微不足道的操作,而不是复杂除法算法的结果。

例如:

private static int generateNo(int randomNo, int value) {
    return randomNo * value & 255;
}

这不可能有负结果,因为& 255保证只能设置结果的低8位,所以结果肯定在[0..255]范围内。

如果您想要结果的一些较低位,那么让乘法先回绕是可以的,如此处(最低 8 位)。如果你想计算 (x * y) MOD p 其中 p 不是二的幂,它不能正常工作,因为然后(在解决 Java 的有符号余数之后)实际计算变成(由于包装)((x * y) MOD 2³²) MOD p。 IFF p 除以 2³²(即当且仅当 p 是不超过 2³² 的二的幂)then 简化为 (x * y) MOD p.

或者更比特级的观点:乘积的比特是"full"乘积的最低32位(两个32位整数的完整乘积有64位),当然如果我们只需要这些位(或它们的某些子集,例如最低的 8 位)就可以了。但如果我们想要的结果取决于乘积的 32 位高位,那么显然我们需要计算这些位。 (x * y) MOD p 其中 p 不是 2 的幂将取决于完整产品的所有位。