【问题标题】:Math.abs returns wrong value for Integer.Min_VALUEMath.abs 为 Integer.Min_VALUE 返回错误值
【发布时间】:2011-07-23 14:38:44
【问题描述】:

这段代码:

System.out.println(Math.abs(Integer.MIN_VALUE));

返回-2147483648

不应该返回绝对值2147483648吗?

【问题讨论】:

    标签: java absolute-value


    【解决方案1】:

    Integer.MIN_VALUE-2147483648,但 32 位整数可以包含的最大值是 +2147483647。尝试在 32 位 int 中表示 +2147483648 将有效地“翻转”到 -2147483648。这是因为,当使用有符号整数时,+2147483648-2147483648 的二进制补码表示是相同的。不过,这不是问题,因为+2147483648 被认为超出范围。

    如需更多了解此问题,您可能需要查看Wikipedia article on Two's complement

    【讨论】:

    • 好吧,不是问题低估了影响,它很可能意味着问题。就我个人而言,我宁愿有一个例外或一个在高级语言中动态增长的数字系统。
    【解决方案2】:

    您指出的行为确实违反直觉。但是,此行为是由javadoc for Math.abs(int) 指定的行为:

    如果参数不是负数,则返回参数。 如果参数是否定的,则返回参数的否定。

    也就是说,Math.abs(int) 的行为应该类似于以下 Java 代码:

    public static int abs(int x){
        if (x >= 0) {
            return x;
        }
        return -x;
    }
    

    也就是说,在否定的情况下,-x

    根据JLS section 15.15.4-x 等于(~x)+1,其中~ 是按位补码运算符。

    要检查这听起来是否正确,我们以-1为例。

    整数值-1 在Java 中可以用十六进制表示为0xFFFFFFFF(使用println 或任何其他方法检查)。以-(-1) 给出:

    -(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
    

    所以,它有效。

    现在让我们试试Integer.MIN_VALUE。知道最小整数可以用0x80000000表示,即第一位设为1,其余31位设为0,我们有:

    -(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1 
                         = 0x80000000 = Integer.MIN_VALUE
    

    这就是Math.abs(Integer.MIN_VALUE) 返回Integer.MIN_VALUE 的原因。还要注意0x7FFFFFFFInteger.MAX_VALUE

    也就是说,我们如何才能避免将来由于这种违反直觉的返回值而出现问题?

    • 我们可以 as pointed out by @Bombe 之前将我们的 ints 转换为 long。然而,我们必须要么

      • 将它们转换回ints,这不起作用,因为 Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
      • 或者继续使用longs 以某种方式希望我们永远不会使用等于Long.MIN_VALUE 的值调用Math.abs(long),因为我们也有Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
    • 我们可以在任何地方使用BigIntegers,因为BigInteger.abs() 确实总是返回一个正值。这是一个不错的选择,虽然比处理原始整数类型要慢一些。

    • 我们可以为Math.abs(int)编写自己的包装器,如下所示:

    /**
     * Fail-fast wrapper for {@link Math#abs(int)}
     * @param x
     * @return the absolute value of x
     * @throws ArithmeticException when a negative value would have been returned by {@link Math#abs(int)}
     */
    public static int abs(int x) throws ArithmeticException {
        if (x == Integer.MIN_VALUE) {
            // fail instead of returning Integer.MAX_VALUE
            // to prevent the occurrence of incorrect results in later computations
            throw new ArithmeticException("Math.abs(Integer.MIN_VALUE)");
        }
        return Math.abs(x);
    }
    
    • 使用整数按位与清除高位,确保结果为非负数:int positive = value & Integer.MAX_VALUE(基本上从Integer.MAX_VALUE 溢出到0 而不是Integer.MIN_VALUE

    最后一点,这个问题似乎已经有一段时间了。参见例如this entry about the corresponding findbugs rule

    【讨论】:

      【解决方案3】:

      这是 Java 文档在 javadoc 中对 Math.abs() 所说的:

      注意,如果参数等于 Integer.MIN_VALUE 的值, 最负的可表示 int 值, 结果是相同的值,其中 是否定的。

      【讨论】:

        【解决方案4】:

        要查看您期望的结果,请将Integer.MIN_VALUE 转换为long

        System.out.println(Math.abs((long) Integer.MIN_VALUE));
        

        【讨论】:

        • 一个可能的解决办法,确实!但是,这并不能解决 Math.abs 通过返回负数违反直觉的事实:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
        • @bernardpaulus,好吧,除了抛出ArithmeticException,它还能做什么?此外,API 文档中清楚地记录了该行为。
        • 您的问题没有很好的答案...我只是想指出,这种行为是错误的来源,使用Math.abs(long) 无法解决。我在这里为我的错误道歉:我认为您建议使用Math.abs(long) 作为修复,当您将其展示为“查看提问者期望的结果”的简单方法时。对不起。
        • 在 Java 15 中使用新方法实际上会引发异常。
        【解决方案5】:

        2147483648在java中不能存储为整数,其二进制表示与-2147483648相同。

        【讨论】:

          【解决方案6】:

          在 Java 15 中对此进行了修复,它将是一个 int 和 long 的方法。他们将出现在课堂上

          java.lang.Math and java.lang.StrictMath
          

          方法。

          public static int absExact(int a)
          public static long absExact(long a)
          

          如果你通过了

          Integer.MIN_VALUE
          

          Long.MIN_VALUE
          

          抛出异常。

          https://bugs.openjdk.java.net/browse/JDK-8241805

          我想看看是否传递了 Long.MIN_VALUE 或 Integer.MIN_VALUE 是否会返回正值,而不是异常。

          【讨论】:

            【解决方案7】:

            但是(int) 2147483648L == -2147483648 有一个负数没有正等值,所以它没有正值。您将看到 Long.MAX_VALUE 的相同行为。

            【讨论】:

              【解决方案8】:

              Math.abs 并不总是适用于大数字我使用我在 7 岁时学到的这个小代码逻辑!

              if(Num < 0){
                Num = -(Num);
              } 
              

              【讨论】:

              • 这里的s 是什么?
              • 对不起,我忘了从我的原始代码中更新它
              • 那么如果Num在sn-p之前等于Integer.MIN_VALUE会导致什么结果呢?
              猜你喜欢
              • 1970-01-01
              • 2011-02-07
              • 2019-02-03
              • 2016-05-13
              • 1970-01-01
              • 2015-06-19
              • 2014-01-16
              • 1970-01-01
              • 2019-10-08
              相关资源
              最近更新 更多