实现数学公式时出现溢出问题

时间:2012-06-04 13:41:54

标签: c++ algorithm numerical-methods integer-overflow

我听说,在计算平均值时,start +(end-start)/ 2与(start + end)/ 2不同,因为后者会导致溢出。我不太明白为什么第二个会导致溢出而第一个不会导致溢出。实现可以避免溢出的数学公式的通用规则是什么。

4 个答案:

答案 0 :(得分:10)

假设您使用的计算机的最大整数值为10,并且您想要计算平均值5和7。

第一种方法(开始+(结束开始)/ 2)给出

5 + (7-5)/2 == 5 + 2/2 == 6

第二种方法(开始+结束)/ 2给出溢出,因为中间12值超过我们接受的最大值10并且“包裹”到其他东西(如果你使用无符号数字,它通常是回到零,但如果您的号码已签名,您可以获得负数!)。

12/2 => overflow occurs => 2/2 == 1

当然,在实际的计算机中,整数溢出的值很大,比如2 ^ 32而不是10,但这个想法是一样的。不幸的是,没有“通用”方法可以摆脱我所知道的溢出,这在很大程度上取决于您使用的是哪种特定算法。然后事件变得更加复杂。根据您使用的数字类型,您可以获得不同的行为,除了上下溢之外还有其他类型的数字错误需要担心。

答案 1 :(得分:3)

你的公式都会溢出,但在不同情况下:

  • (start+end)(start+end)/2都接近范围同一侧的整数限制时,start公式的end部分会溢出(即均为正值)或两者都是否定的。)
  • (end-start)为正且start+(end-start)/2为负数时,start公式的end部分会溢出,并且这两个值都接近可表示整数的各自末尾值。

没有“通用”规则,你可以根据具体情况进行操作:查看公式的部分内容,考虑可能导致溢出的情况,并想出办法避免它。例如,当您使用相同符号对值进行平均时,可以显示start+(end-start)/2公式以避免溢出。

这是艰难的方式;简单的方法是对中间结果使用更高容量的表示。例如,如果您使用long long代替int进行中间计算并仅在完成后将结果复制回int,则可以避免溢出,假设最终结果适合int

答案 2 :(得分:1)

在处理整数时,您可能会在采用此类策略时关注integer overflow

请注意,使用公式b+(b-a)/2,您需要确保a <= b。否则,您可能会在可能的值范围的下限处得到相同的问题。想想a/2+b/2。然而,这种方法也存在其他缺点。


处理浮点数时会出现另一个问题,catastrophic cancellation。由于浮点表示的有效位数有限,添加大数字时精度会丢失(即使这只是一个中间步骤)。

要解决这个数值稳定性问题,例如可以使用此算法(稍微改编自wikipedia):

def online_mean(data):
  n = 0
  mean = 0

  for x in data:
    n = n + 1
    delta = x - mean
    mean = mean + delta/n

  return mean

我不知何故觉得与你上面提到的公式存在关系......

答案 3 :(得分:0)

在二进制搜索中,我们将编写以下代码:

if(start > end){
   return;
}
int mid = start + (end - start) / 2;

使用start + (end - start) / 2,我们可以避免@dasblinkenlight指出的问题

如果我们使用(start + end) / 2,它会溢出,如dasblinkenlight

所示