Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

muldiv.gno

2.18 Kb · 63 lines
 1package xmath
 2
 3import "math/bits"
 4
 5// maxInt64 is the largest int64. Unexported and spelled out because xmath
 6// already exports MaxInt64 as the max-of-two helper, and a constant sharing
 7// that name would shadow it.
 8const maxInt64 = int64(9223372036854775807)
 9
10// MulDiv returns a*b/c computed through a 128-bit intermediate, so the product
11// does not have to fit in an int64. It is the proportional-share calculation:
12// "this account's cut of the pot", "this stake's slice of the rewards".
13//
14// The naive a*b/c silently wraps when a*b exceeds an int64, and it wraps to a
15// plausible-looking number rather than to an obvious one, which is how a payout
16// split leaks money without anything failing. Ten realms deployed on gnoland-1
17// carry their own copy of this function for exactly that reason, and every one
18// of them reaches for math/bits the same way.
19//
20// MulDiv panics rather than returning a wrong number:
21//
22//   - a or b negative, or c at or below zero: the domain is unsigned ratios,
23//     and a negative share is a caller bug, not a value to propagate.
24//   - the quotient not fitting in an int64.
25//
26// Rounding is toward zero, like integer division. See [MulDivUp] when the
27// remainder must favour the payer instead.
28func MulDiv(a, b, c int64) int64 {
29	if a < 0 || b < 0 {
30		panic("xmath: MulDiv on a negative operand")
31	}
32	if c <= 0 {
33		panic("xmath: MulDiv by a non-positive denominator")
34	}
35	hi, lo := bits.Mul64(uint64(a), uint64(b))
36	if hi >= uint64(c) {
37		panic("xmath: MulDiv quotient overflows int64")
38	}
39	q, _ := bits.Div64(hi, lo, uint64(c))
40	if q > uint64(maxInt64) {
41		panic("xmath: MulDiv quotient overflows int64")
42	}
43	return int64(q)
44}
45
46// MulDivUp is [MulDiv] rounding away from zero when the division is not exact.
47//
48// Which way to round is a money question, not a style one: rounding a fee down
49// and a payout up is how a pool pays out more than it holds. Use MulDiv for
50// what someone receives and MulDivUp for what someone owes.
51func MulDivUp(a, b, c int64) int64 {
52	q := MulDiv(a, b, c)
53	hi, lo := bits.Mul64(uint64(a), uint64(b))
54	_, rem := bits.Div64(hi, lo, uint64(c))
55	if rem != 0 {
56		if q == maxInt64 {
57			panic("xmath: MulDivUp quotient overflows int64")
58		}
59		q++
60	}
61	return q
62}
63