go / checked

I use checked arithmetic to detect integer overflow before it causes silent bugs.

The problem

Go's integer arithmetic silently overflows:

var a int64 = math.MaxInt64
b := a + 1 // -9223372036854775808, no error

For financial calculations, counters, or any code where overflow would be a serious bug, you need explicit checks.

The pattern

package checked

import "math"

func AddInt64(a, b int64) (int64, bool) {
	if (b > 0 && a > math.MaxInt64-b) ||
		(b < 0 && a < math.MinInt64-b) {
		return 0, false
	}
	return a + b, true
}

func SubInt64(a, b int64) (int64, bool) {
	if (b > 0 && a < math.MinInt64+b) ||
		(b < 0 && a > math.MaxInt64+b) {
		return 0, false
	}
	return a - b, true
}

func MulInt64(a, b int64) (int64, bool) {
	if (a > 0 && b > 0 && a > math.MaxInt64/b) ||
		(a > 0 && b < 0 && b < math.MinInt64/a) ||
		(a < 0 && b > 0 && a < math.MinInt64/b) ||
		(a < 0 && b < 0 && b < math.MaxInt64/a) {
		return 0, false
	}
	return a * b, true
}

For unsigned integers, the checks are simpler:

func AddUint64(a, b uint64) (uint64, bool) {
	if math.MaxUint64-a < b {
		return 0, false
	}
	return a + b, true
}

func MulUint64(a, b uint64) (uint64, bool) {
	if b > 0 && a > math.MaxUint64/b {
		return 0, false
	}
	return a * b, true
}

Usage

total, ok := checked.AddInt64(balance, deposit)
if !ok {
	return errors.New("balance overflow")
}

product, ok := checked.MulUint64(price, quantity)
if !ok {
	return errors.New("total exceeds maximum")
}

When to use

When not to use

For unsigned integers, math/bits provides hardware-accelerated overflow detection that compiles to single instructions.

If overflow is always a bug, combine with the must pattern.

← All articles