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

fixedpoint.gno

2.31 Kb · 114 lines
  1// Package fixedpoint provides deterministic decimal arithmetic with a
  2// fixed number of decimal places, backed by int64. gno.land has no
  3// float type reachable from consensus-critical code (and even if it
  4// did, floats aren't deterministic across platforms), so any realm
  5// doing price/ratio/percentage math needs something like this instead
  6// of drifting into ad-hoc basis-points arithmetic scattered everywhere.
  7//
  8// A Fixed value stores its number scaled by 10^Decimals in Raw. All
  9// arithmetic is overflow-checked via math/overflow and panics rather
 10// than silently wrapping -- a wrapped overflow in financial math is a
 11// far worse failure mode than a loud panic.
 12package fixedpoint
 13
 14import (
 15	"math/overflow"
 16
 17	"gno.land/p/nt/ufmt/v0"
 18)
 19
 20const Decimals = 6
 21
 22const Scale int64 = 1000000
 23
 24type Fixed struct {
 25	Raw int64
 26}
 27
 28func FromInt(i int64) Fixed {
 29	raw, ok := overflow.Mul64(i, Scale)
 30	if !ok {
 31		panic("fixedpoint: FromInt overflowed")
 32	}
 33	return Fixed{Raw: raw}
 34}
 35
 36func FromRaw(raw int64) Fixed {
 37	return Fixed{Raw: raw}
 38}
 39
 40func (f Fixed) Add(other Fixed) Fixed {
 41	raw, ok := overflow.Add64(f.Raw, other.Raw)
 42	if !ok {
 43		panic("fixedpoint: Add overflowed")
 44	}
 45	return Fixed{Raw: raw}
 46}
 47
 48func (f Fixed) Sub(other Fixed) Fixed {
 49	raw, ok := overflow.Sub64(f.Raw, other.Raw)
 50	if !ok {
 51		panic("fixedpoint: Sub overflowed")
 52	}
 53	return Fixed{Raw: raw}
 54}
 55
 56func (f Fixed) Mul(other Fixed) Fixed {
 57	product, ok := overflow.Mul64(f.Raw, other.Raw)
 58	if !ok {
 59		panic("fixedpoint: Mul overflowed")
 60	}
 61	return Fixed{Raw: product / Scale}
 62}
 63
 64func (f Fixed) Div(other Fixed) Fixed {
 65	if other.Raw == 0 {
 66		panic("fixedpoint: division by zero")
 67	}
 68	scaled, ok := overflow.Mul64(f.Raw, Scale)
 69	if !ok {
 70		panic("fixedpoint: Div overflowed")
 71	}
 72	return Fixed{Raw: scaled / other.Raw}
 73}
 74
 75func (f Fixed) IsZero() bool {
 76	return f.Raw == 0
 77}
 78
 79func (f Fixed) IsNegative() bool {
 80	return f.Raw < 0
 81}
 82
 83func (f Fixed) Cmp(other Fixed) int {
 84	if f.Raw < other.Raw {
 85		return -1
 86	}
 87	if f.Raw > other.Raw {
 88		return 1
 89	}
 90	return 0
 91}
 92
 93func (f Fixed) String() string {
 94	raw := f.Raw
 95	negative := raw < 0
 96	if negative {
 97		raw = -raw
 98	}
 99
100	whole := raw / Scale
101	frac := raw % Scale
102
103	fracStr := ufmt.Sprintf("%d", frac)
104	for len(fracStr) < Decimals {
105		fracStr = "0" + fracStr
106	}
107
108	sign := ""
109	if negative {
110		sign = "-"
111	}
112
113	return ufmt.Sprintf("%s%d.%s", sign, whole, fracStr)
114}