Detect signed int overflow in Go

huangapple go评论71阅读模式
英文:

Detect signed int overflow in Go

问题

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

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

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

我该如何高效地检查c是否溢出?

我考虑过始终将值转换为64位进行计算,然后在可能的情况下再次缩小大小,但这似乎对于作为基本算术运算语言的基本部分来说既昂贵又浪费内存。

英文:

I'm building a Lisp, and I want 32 bit integers to automatically switch to 64 bit integers if a computation would cause them to otherwise overflow. And likewise, for 64 bit overflows, switch to arbitrarily sized integers.

The problem I have is that I don't know what the "correct" way is to detect an integer overflow.

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

How can I efficiently check if c overflowed?

I have considered always converting to 64 bit values to do the calculation, then down-sizing again afterwards when possible, but that seems expensive and memory wasteful for something that is as primitive and core to the language as basic arithmetic.

答案1

得分: 16

例如,要检测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 {
		// 处理溢出
		fmt.Println(err, a, b, c)
	}
}

输出:

integer overflow 2147483327 2147483327 0
英文:

For example, to detect 32-bit integer overflow for addition,

package main

import (
	&quot;errors&quot;
	&quot;fmt&quot;
	&quot;math&quot;
)

var ErrOverflow = errors.New(&quot;integer overflow&quot;)

func Add32(left, right int32) (int32, error) {
	if right &gt; 0 {
		if left &gt; math.MaxInt32-right {
			return 0, ErrOverflow
		}
	} else {
		if left &lt; 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)
	}
}

Output:

integer overflow 2147483327 2147483327 0

答案2

得分: 1

对于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
英文:

For 32 bit integers, the standard way is as you said, to cast to 64 bit, then down size again [1]:

package main

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

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

However if you don't like that, you can use some bit operations [2]:

func add32(x, y int32) (int32, int32) {
   sum := x + y
   return sum, x &amp; y | (x | y) &amp;^ sum &gt;&gt; 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

huangapple
  • 本文由 发表于 2015年11月11日 07:37:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/33641717.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定