【问题标题】:Detect signed int overflow in Go在 Go 中检测有符号整数溢出
【发布时间】:2015-11-10 23:37:00
【问题描述】:

我正在构建一个 Lisp,如果计算会导致它们溢出,我希望 32 位整数自动切换到 64 位整数。同样,对于 64 位溢出,切换到任意大小的整数。

我的问题是我不知道检测整数溢出的“正确”方法是什么。

a, b := 2147483647, 2147483647
c := a + b

如何有效地检查 c 是否溢出?

我考虑过总是转换为 64 位值来进行计算,然后在可能的情况下再次缩小大小,但这对于像基本算术这样原始和核心的语言来说似乎很昂贵且内存浪费。

【问题讨论】:

  • 我怀疑拳击是你最好的选择。语言中没有精确地保持快速加法的机制。
  • 我认为在大多数现代系统中额外的 4 个字节几乎可以忽略不计,在大多数情况下只使用 64 位和 32 位整数(大型数组等例外)。至于运行时自动检测溢出而不诉诸汇编,并且如果您的环境没有抛出您可以捕获的异常,这并非易事。恕我直言。
  • 转换为 64 位 仅用于计算 基本上没有内存影响,因为您一次只保存恒定数量的升值值。例如,OpenJDK 的 Math.multiplyExact(int, int) 就是这样做的。
  • @captncraig 它可以在没有开销的情况下完成(除了稍微丑陋的代码)。处理器已经免费计算了每次加法的溢出位;您所需要的只是一种告诉编译器您对它感兴趣的方法。现在 GCC 有 __builtin_add_overflowif(__builtin_add_overflow(a,b,c)) { ... 可以直接编译为 addjo,或者针对给定平台的任何内容。
  • @d11wtq 老实说,这里合理的做法是始终使用 64 位。 32 位 CPU 正在迅速消失,而在 64 位机器上,使用 64 位整数并没有 CPU 成本,而且与系统其他部分的开销相比,内存成本可能微不足道。

标签: go integer-overflow


【解决方案1】:

例如,要检测 32 位整数溢出进行加法,

package main

import (
    "errors"
    "fmt"
    "math"
)

var ErrOverflow = errors.New("integer overflow")

func Add32(left, right int32) (int32, error) {
    if right > 0 {
        if left > math.MaxInt32-right {
            return 0, ErrOverflow
        }
    } else {
        if left < math.MinInt32-right {
            return 0, ErrOverflow
        }
    }
    return left + right, nil
}
func main() {
    var a, b int32 = 2147483327, 2147483327
    c, err := Add32(a, b)
    if err != nil {
        // handle overflow
        fmt.Println(err, a, b, c)
    }
}

输出:

integer overflow 2147483327 2147483327 0

【讨论】:

  • 谢谢!我想我可能有一个更短的解决方案,但不确定是否存在不起作用的边缘情况:((c &lt; a) != (b &lt; 0)),其中c := a + b
【解决方案2】:

对于 32 位整数,标准方法是如您所说,转换为 64 位,然后再次缩小大小 [1]:

package main

func add32(x, y int32) (int32, int32) {
   sum64 := int64(x) + int64(y)
   return x + y, int32(sum64 >> 31)
}

func main() {
   {
      s, c := add32(2147483646, 1)
      println(s == 2147483647, c == 0)
   }
   {
      s, c := add32(2147483647, 1)
      println(s == -2147483648, c == 1)
   }
}

但是如果你不喜欢这样,你可以使用一些位操作[2]:

func add32(x, y int32) (int32, int32) {
   sum := x + y
   return sum, x & y | (x | y) &^ sum >> 30
}
  1. https://github.com/golang/go/blob/go1.16.3/src/math/bits/bits.go#L368-L373
  2. https://github.com/golang/go/blob/go1.16.3/src/math/bits/bits.go#L380-L387

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-29
    相关资源
    最近更新 更多