【问题标题】:How do I fix wrong numbers produced by integer overflow?如何修复整数溢出产生的错误数字?
【发布时间】:2011-06-15 11:16:05
【问题描述】:

我有一个错误导致整数溢出,导致错误(负)时间戳被写入数据库。 代码已经修复,但我也想修复错误的数据

我想,我可以只取错误的结果并添加 Integer.MAX_VALUE,但这似乎不起作用,它给我留下了很高的价值。我在下面的代码 sn-p 中有offset 值,但输入值没有存储。

以下代码重现了该错误:

@Test
public void testArexxConversion()
{
    // The input values represent seconds since midnight, Jan 1, 2000 UTC
    final int sample = 361450072; // A sample input value drawn from production
    // I use the offset from the UNIX epoch to convert the vakue to UNIX seconds
    final int offset = 946684800; // midnight, Jan 01 2000 UTC in UNIX seconds
    // This was the buggy line in my code, the assertion will fail
    long result = (sample + offset) * 1000;
    // Prints 'Result is negative: -1830153280'
    Assert.assertTrue(result > 0, String.format("Result is negative: %d", result));
    // This is for comparison
    Date dt = new Date(offset * 1000);
    Assert.assertEquals(dt.getTime() + sample * 1000, result);
}

【问题讨论】:

  • 另外,为什么首先会发生溢出?将上面的两个数字相加不会溢出,它是乘法,我有点预计这已经在 long 上完成了,因为那是结果类型。

标签: java integer-overflow


【解决方案1】:

如何修复数据库中的错误

要修复数据库中的错误,您可以对所有错误数据执行以下操作:

long new_result = old_buggy_result + 1309965025280L;

常数是这样找到的:

  1. 检查错误result
  2. 找出正确的result 值应该是什么?
  3. 添加到 buggy result 值以找到正确的`结果。

但这只有在您已将sampleoffset 保存在数据库或其他位置时才有可能。

否则,这取决于原始计算期间发生的换行次数:

long size_of_int = (long)Math.pow(2, 32);
int number_of_wraps = 305 // Only correct in your example!
                          // You can't deduct the number of wraps from
                          // the wrong value alone, because that information
                          // is lost in the modulo (the "wrap")
long correct_number = wrong_number + size_of_int * number_of_wraps;

如果您的数据库中的数字与您的样本值足够接近,这意味着您可以执行上述操作,使用 305 作为换行次数。

错误说明(供未来读者使用)

这里的操作:

 (sample + offset) * 1000;

使用int 而不是long 计算。但结果是“太大”而无法保存在 int 变量中。这就是你有溢出的原因。

改成:

  ((long) sample + offset) * 1000L;

所以现在+* 操作将使用long 值完成,结果将是一个不会溢出的long 值。

【讨论】:

  • 谢谢,但该错误已修复。我的问题是:如何修复数据库中已有的错误数字?
  • @Luzhin - 感谢您的更新。我有偏移值,因为它是固定的,但输入值没有保留,否则我将通过固定代码运行它们。
  • @Luzhin 我明白了。这取决于您使用+* 时已完成的环绕次数。如果您的数据库中的数字接近此样本值:361450072,例如361450972361451290。你可以像下面这样:` long diff = (long)Integer.MAX_VALUE - Integer.MIN_VALUE + 1; long valid_result = buggy_result + 305 * diff;`
  • @Luzhin - 谢谢,这应该会有所帮助,它还帮助我了解了溢出期间实际发生的情况。错误的结果足够接近以具有相同数量的换行。我会将其编辑为答案并接受它。
  • @Hanno Fietz .. 不错!谢谢你:)
【解决方案2】:

应该是这样的:

long result = ... ; // bad negative from database
long new_result = (long)((int)result - Integer.MAX_VALUE) + Integer.MAX_VALUE;

【讨论】:

  • 这将强制整数反向溢出,将数量留在 MAX_VALUE 之上并再次添加 MAX_VALUE 以获得正确的值,对吗?可能比我的解决方案更优雅: new_result = ( ( 2L + max - (old_buggy_result * -1L) ) + max); //使用非溢出算法做同样的事情。
  • 这只有在它只溢出一次时才有效。但是,据我所知,这个错误代码不会溢出不止一次
  • 正如我接受的答案中所指出的那样,样本值的错误代码溢出了 305 次。实际值足够接近以具有相同的换行次数。
  • 是的,如果溢出只发生一次,这将起作用。对于自定义溢出次数(无论如何,通常无法正确扣除),公式为:long new_result = (long)((int)result - Integer.MAX_VALUE) + Integer.MAX_VALUE + (long) 2<<Integer.SIZE * (numOverflows-1);
【解决方案3】:

替换此行。

long result = (long)(sample + offset) * 1000L;

【讨论】:

  • 谢谢,但该错误已修复。我的问题是:如何修复数据库中已有的错误数字?
  • 不需要转换为 long。
  • @Hanno,直到我在这里阅读您的评论才完全清楚,您正在修复不良数据。也许您可以编辑您的问题以具体说明这一点。
猜你喜欢
  • 2017-05-01
  • 1970-01-01
  • 2023-03-24
  • 2019-08-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 2012-01-04
相关资源
最近更新 更多