两个整数的乘法溢出导致负数

时间:2011-08-27 15:14:30

标签: java integer overflow

考虑Java语言规范中的这个片段。

class Test {
    public static void main(String[] args) {
        int i = 1000000;
        System.out.println(i * i);
        long l = i;
        System.out.println(l * l);
    }
}

输出

-727379968
1000000000000

为什么-727379968的结果为(i*i)?理想情况下应该是1000000000000。

我知道Integer的范围是从-2147483648到2147483647.所以显然是1000000000000 不在给定范围内。

为什么结果会变成-727379968

5 个答案:

答案 0 :(得分:8)

Java(与目前大多数计算机体系结构一样)使用名为two's complement arithmetic的东西,它使用整数的最高位来表示数字是负数。如果你乘以两个大数字,你最终会得到一个如此大的数字,它设置最高位,结果最终为负数。

答案 1 :(得分:6)

您可能希望将Integer overflow视为一般概念。 根据语言的不同,溢出和下溢的处理方式也不同。这是关于Integer overflow and underflow in Java的文章。

至于Java语言中的原因,与往常一样,它是语言设计的简单性和性能之间的权衡。但是在Java puzzlers(谜题3)中,作者批评了Java中溢出是静默的事实:

  

语言设计师的教训是,值得减少   无声溢出的可能性。这可以通过提供支持来完成   对于不会无声溢出的算术。程序可能会抛出   一个例外而不是溢出,就像Ada一样,或者他们可以切换   根据需要自动更大的内部表示以避免   溢出,Lisp也是如此。这两种方法都可能具有性能   与他们相关的处罚。另一种减少可能性的方法   静默溢出是为了支持目标类型,但这会增加   类型系统非常复杂。

答案 2 :(得分:3)

其他一些答案正确解释了为什么会发生这种情况(即签署了两个恭维二进制逻辑)。

问题的实际解决方案以及如何在使用非常大的数字时使用Java获得正确的答案是使用BigInteger类,它也适用于长值。

package com.craigsdickson.scratchpad;

import java.math.BigInteger;

public class BigIntegerExample {

    public static void main(String[] args) {

        int bigInt = Integer.MAX_VALUE;
        // prints incorrect answer
        System.out.println(bigInt * bigInt);

        BigInteger bi = BigInteger.valueOf(bigInt);
        // prints correct answer
        System.out.println(bi.multiply(bi));

        long bigLong = Long.MAX_VALUE;
        // prints incorrect answer
        System.out.println(bigLong * bigLong);

        BigInteger bl = BigInteger.valueOf(bigLong);
        // prints correct answer
        System.out.println(bl.multiply(bl));

    }

}

答案 3 :(得分:2)

让我们看看二进制文件:

1000000是1111 0100 0010 0100 0000 1000000000000是1110 1000 1101 0100 1010 0101 0001 0000 0000 0000

然而,4位的前两个部分不适合int(因为int在Java中是32位宽),因此它们是已删除,仅保留1101 0100 1010 0101 0001 0000 0000 0000,即-727379968

换句话说,结果会溢出int,你会得到剩下的东西。

答案 4 :(得分:0)

在其他答案中已经解释了出现整数溢出的原因。

确保计算中长算术的一种实用方法是使用带有long后缀的数字文字,将文字声明为jshell> 100000 * 100000 $1 ==> -727379968

溢出的普通整数乘法:

l

乘法,其中一个被乘数具有jshell> 100000 * 100000l $1 ==> 1000000000000 后缀,不会溢出:

long

请注意,-9,223,372,036,854,775,808也容易出现溢出,但范围要大得多,从9,223,372,036,854,775,807scale_fill_gradientn