/p/moul/xmath/v1
gno.land/p/moul/xmath/v1
Max, Min, Clamp, Abs and Sign for every built-in numeric type, one
concrete function per type. Gno has no generics, so the alternative is writing
the same three-line helper again in each realm.
1xmath.MaxInt(3, 7) // 7
2xmath.ClampInt64(x, 0, 100) // x, bounded to [0,100]
3xmath.AbsFloat64(-2.5) // 2.5
4xmath.SignInt(-9) // -1
| type | functions |
|---|---|
int, int8, int16, int32, int64 |
Max, Min, Clamp, Abs, Sign |
uint, uint8, uint16, uint32, uint64 |
Max, Min, Clamp |
float32, float64 |
Max, Min, Clamp, Abs, Sign |
Unsigned types get no Abs or Sign: both are trivially themselves and 1, and
offering them would only invite an overflow bug at the call site.
The name is the type suffix: MaxInt8, MinUint64, ClampFloat32.
Sign returns -1, 0 or 1. Clamp does not validate its bounds: calling it
with min > max returns one bound or the other depending on the value, so
check the bounds yourself if they are computed.
Generated
xmath.gen.gno and xmath.gen_test.gno are written by
tools/xmathgen and carry a DO NOT EDIT header.
Edit the generator, never the output:
1go -C tools run ./xmathgen # rewrite both files
2go -C tools run ./xmathgen -check # fail if they are stale
Adding a type is one row in the types table in tools/xmathgen/main.go; it
expands to 5 functions plus their tests.
The header used to name a generator.go that was never committed, so it was a
rule nobody could follow. The generator above was reconstructed from the output
it describes and reproduces both committed files byte for byte, which
go test ./tools/xmathgen asserts on every run.
MulDiv: the proportional share, through a 128-bit intermediate
Hand-written rather than generated, in muldiv.gno, because it is one function
and not a per-type family.
1xmath.MulDiv(pot, stake, total) // this stake's cut, rounded toward zero
2xmath.MulDivUp(pot, stake, total) // the same ratio, rounded away from zero
a*b/c computes a*b first, and an int64 product that does not fit wraps
silently to a plausible number. Measured with a = 2^62, b = 4, c = 8:
the true share is 2305843009213693952 and the naive form answers 0,
because 2^62 * 4 is exactly 2^64 and wraps to zero. Nothing fails, nothing
logs, and a payout split is simply wrong.
Ten realms deployed on gnoland-1 carry their own copy of this, all reaching
for math/bits the same way: bubblerumble (several versions) and kourt,
whose mulDiv128 is the same function with its own panic strings. It had no
owner in p/moul or p/nt until now.
MulDiv panics rather than returning a wrong number: on a negative operand, on
a denominator at or below zero, and on a quotient that does not fit.
MulDivUp exists because rounding is a money question, not a style one.
Rounding a fee down and a payout up is how a pool pays out more than it holds.
Use MulDiv for what someone receives and MulDivUp for what someone owes;
TestRoundingDirectionIsTheMoneyQuestion pins that they never differ by more
than one and never cross.
Why v1 even though adding a function is non-breaking
It is non-breaking for this repo, and that is not the constraint that
matters. p/ packages are always public, and a live public path can never be
redeployed, so functions added in place at v0 reach main and never reach the
chain. v1 is the only way MulDiv becomes importable by anybody.
The bump is free here: xmath has zero importers, in this repo and across
every package deployed on gnoland-1. v0 stays resolvable for nobody in
particular.
Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.
⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.