【问题标题】:Java MOD operator returns negative valueJava MOD 运算符返回负值
【发布时间】:2019-04-14 15:29:29
【问题描述】:

我有这个方法:

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

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

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

谁能解释一下。

【问题讨论】:

  • 乘法超出整数范围。
  • 而模运算符的作用类似于除法,所以如果一个参数是否定的,它会给出否定的答案。

标签: java negative-number mod


【解决方案1】:

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

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

【讨论】:

  • 这行得通,但我认为向 OP 解释 为什么 行得通会很有用。
【解决方案2】:

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

例如:

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

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

如果你想要结果的一些低位,首先让乘法换行是可以的,就像这里(最低的 8 位)。如果您想计算(x * y) MOD p,其中p 不是2 的幂,则它不能正常工作,因为(在处理Java 的有符号余数之后)实际计算变为(由于包装)((x * y) MOD 2³²) MOD p。 IFF p 除以 2³²(即 iff p 是不超过 2³² 的 2 的幂)然后简化为 (x * y) MOD p

或者使用更多位级别的视图:乘积的位是“完整”乘积的最低 32 位(两个 32 位整数的完整乘积有 64 位),当然如果我们只需要这些位(或其中的一些子集,例如最低的 8 个),那很好。但如果我们想要的结果取决于乘积的高 32 位,那么显然我们需要计算这些位。 (x * y) MOD p 其中p 不是 2 的幂将取决于完整产品的所有位。

【讨论】:

    【解决方案3】:

    17719*197920 = 3506944480,大于Integer.MAX_VALUE

    因此,乘法超出了int的范围,结果为-788022816

    因此,取模会得到否定的结果。

    【讨论】:

      【解决方案4】:

      这里有两件事在起作用。

      1. 将两个ints 相乘会得到一个大于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;
      }
      

      【讨论】:

      • 注意Math.abs 在异常情况下会产生否定结果。此外,通常,整数溢出后的结果将是无意义的。在这种特殊情况下,您可以使用(randomNo*value) & 0xff
      • 哈,你指的是边缘情况Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE?我从来没有想过,但这实际上很有趣,在这种情况下将是一个惊人的(并且完全可能的)错误。我将编辑答案以建议在 result 上使用 Math.abs。太好了。
      • 事实上,OP 真的应该更多地考虑这个函数应该做什么,并且可能对整数溢出和模运算符进行一些研究。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-24
      • 1970-01-01
      • 1970-01-01
      • 2016-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多