2

我的问题与此有关 如何检查Java中的两个数字相乘是否会导致溢出?

在我的应用程序中,x 和 y 是即时计算的,在我的公式中,我必须将 x 和 y 相乘。

     int x=64371;
     int y=64635;

     System.out.println((x*y));

我得到错误的输出-134347711

我可以通过改变变量xy类型int来快速修复上述情况,long并获得上述情况的正确答案。但是,没有任何保证,xy不会超过最大容量long

问题

  1. 为什么我在这里得到一个负数,即使我没有将最终结果存储在任何变量中?(出于好奇

  2. 因为,我不会提前知道 x 和 y 的值,有没有更快的方法来避免这种溢出。也许通过在整个应用程序运行中将所有 x 和 y 除以某个大常数,或者我应该log在将它们相乘之前取 x 和 y?(实际问题

编辑:

澄清

该应用程序在一个大数据集上运行,这需要数小时才能完成。有一个不太慢的解决方案会更好。

由于最终结果用于比较(它们只需要与原始结果成一定比例),如果最终值有+-5%的误差是可以接受的,如果这会带来巨大的性能提升。

4

6 回答 6

6

如果您知道数字可能很大,请BigInteger改用。这保证不会溢出,然后您可以检查结果是否太大而无法放入intor long,或者您可以直接使用该BigInteger值。

BigInteger是一个任意精度的类,所以它会比使用直接原始值(它可能存储在处理器寄存器中)要慢,所以要弄清楚你是否真的会溢出 a longint一次int总是适合 a long),然后选择BigInteger您的域是否真的需要它。

于 2013-10-17T18:52:49.873 回答
5
  1. 由于整数溢出,您得到一个负数:使用二进制补码表示,Java 将最高有效位设置1为的任何整数解释为负数。
  2. 有一些非常聪明的方法涉及位操作,用于检测加法或减法会导致上溢或下溢的情况。如果您不知道您的结果会有多大,最好切换到BigInteger. 但是,您的代码看起来会非常不同,因为 Java 缺少运算符重载工具,这会使BigInteger对象的数学运算看起来很熟悉。代码也会有点慢。但是,将保证您不会出现上溢和下溢。

编辑 :

如果这会带来巨大的性能提升,那么最终值中存在 +-5% 的误差是可以接受的。

+-5% 的误差是一个巨大的误差余量!如果这在您的系统中确实可以接受,那么使用double甚至float可以工作。这些类型不精确,但它们的范围比 a 的范围大得多int,而且它们不会那么容易溢出。但是,您必须非常小心,因为浮点数据类型本质上是不精确的。您需要始终牢记数据的表示方式,以避免常见的精度问题。

于 2013-10-17T18:54:52.357 回答
1

为什么我在这里得到一个负数,即使我没有将最终结果存储在任何变量中?(出于好奇)

x 和 y 是 int 类型。当您将它们相乘时,它们会暂时放入一块内存中。其类型由原稿的类型决定。int*int 将始终产生一个 int。就算溢出来。如果您将其中一个转换为长整数,那么它将为乘法创建一个长整数,并且您不会溢出。

因为,我不知道 x 和 y 的值,有没有更快的方法来避免这种溢出。也许通过在整个应用程序运行中将所有 x 和 y 除以某个大常数,或者我应该在 x 和 y 相乘之前记录它们?(实际问题)

如果 x 和 y 是正数,那么您可以检查

if(x*y<0)
{
    //overflow
}
else
{
    //do something with x*y
}

不幸的是,这不是万无一失的。您可能会再次超出正数。例如:System.out.println(Integer.MAX_VALUE * 3);将输出:2147483645。

但是,这种技术总是适用于添加 2 个整数。

正如其他人所说,BigInteger 肯定不会溢出。

于 2013-10-17T18:57:32.943 回答
0

负值只是 (64371 * 64635) - 2^32。Java 在运行时不执行加宽原始转换。

于 2013-10-17T18:56:42.623 回答
0

整数的乘法总是导致整数,即使它没有存储在变量中。您的产品是 4160619585,它需要无符号的 32 位(Java 没有)或更大的字长(或 BigInteger,似乎有人已经提到过)。

您可以改为添加日志,但是当您尝试 exp 结果时,您会得到一个无法正确四舍五入为有符号 32 位的数字。

于 2013-10-17T18:57:05.057 回答
0

由于两个被乘数都是int,因此使用long通过强制转换进行乘法将避免在您的特定情况下溢出:

System.out.println(x * (long) y);

您不想使用对数,因为超越函数很慢并且浮点运算不精确 - 结果可能不等于正确的整数答案。

于 2013-10-17T19:08:56.683 回答