mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
615 lines
16 KiB
615 lines
16 KiB
// Package inf (type inf.Dec) implements "infinite-precision" decimal |
|
// arithmetic. |
|
// "Infinite precision" describes two characteristics: practically unlimited |
|
// precision for decimal number representation and no support for calculating |
|
// with any specific fixed precision. |
|
// (Although there is no practical limit on precision, inf.Dec can only |
|
// represent finite decimals.) |
|
// |
|
// This package is currently in experimental stage and the API may change. |
|
// |
|
// This package does NOT support: |
|
// - rounding to specific precisions (as opposed to specific decimal positions) |
|
// - the notion of context (each rounding must be explicit) |
|
// - NaN and Inf values, and distinguishing between positive and negative zero |
|
// - conversions to and from float32/64 types |
|
// |
|
// Features considered for possible addition: |
|
// + formatting options |
|
// + Exp method |
|
// + combined operations such as AddRound/MulAdd etc |
|
// + exchanging data in decimal32/64/128 formats |
|
// |
|
package inf // import "gopkg.in/inf.v0" |
|
|
|
// TODO: |
|
// - avoid excessive deep copying (quo and rounders) |
|
|
|
import ( |
|
"fmt" |
|
"io" |
|
"math/big" |
|
"strings" |
|
) |
|
|
|
// A Dec represents a signed arbitrary-precision decimal. |
|
// It is a combination of a sign, an arbitrary-precision integer coefficient |
|
// value, and a signed fixed-precision exponent value. |
|
// The sign and the coefficient value are handled together as a signed value |
|
// and referred to as the unscaled value. |
|
// (Positive and negative zero values are not distinguished.) |
|
// Since the exponent is most commonly non-positive, it is handled in negated |
|
// form and referred to as scale. |
|
// |
|
// The mathematical value of a Dec equals: |
|
// |
|
// unscaled * 10**(-scale) |
|
// |
|
// Note that different Dec representations may have equal mathematical values. |
|
// |
|
// unscaled scale String() |
|
// ------------------------- |
|
// 0 0 "0" |
|
// 0 2 "0.00" |
|
// 0 -2 "0" |
|
// 1 0 "1" |
|
// 100 2 "1.00" |
|
// 10 0 "10" |
|
// 1 -1 "10" |
|
// |
|
// The zero value for a Dec represents the value 0 with scale 0. |
|
// |
|
// Operations are typically performed through the *Dec type. |
|
// The semantics of the assignment operation "=" for "bare" Dec values is |
|
// undefined and should not be relied on. |
|
// |
|
// Methods are typically of the form: |
|
// |
|
// func (z *Dec) Op(x, y *Dec) *Dec |
|
// |
|
// and implement operations z = x Op y with the result as receiver; if it |
|
// is one of the operands it may be overwritten (and its memory reused). |
|
// To enable chaining of operations, the result is also returned. Methods |
|
// returning a result other than *Dec take one of the operands as the receiver. |
|
// |
|
// A "bare" Quo method (quotient / division operation) is not provided, as the |
|
// result is not always a finite decimal and thus in general cannot be |
|
// represented as a Dec. |
|
// Instead, in the common case when rounding is (potentially) necessary, |
|
// QuoRound should be used with a Scale and a Rounder. |
|
// QuoExact or QuoRound with RoundExact can be used in the special cases when it |
|
// is known that the result is always a finite decimal. |
|
// |
|
type Dec struct { |
|
unscaled big.Int |
|
scale Scale |
|
} |
|
|
|
// Scale represents the type used for the scale of a Dec. |
|
type Scale int32 |
|
|
|
const scaleSize = 4 // bytes in a Scale value |
|
|
|
// Scaler represents a method for obtaining the scale to use for the result of |
|
// an operation on x and y. |
|
type scaler interface { |
|
Scale(x *Dec, y *Dec) Scale |
|
} |
|
|
|
var bigInt = [...]*big.Int{ |
|
big.NewInt(0), big.NewInt(1), big.NewInt(2), big.NewInt(3), big.NewInt(4), |
|
big.NewInt(5), big.NewInt(6), big.NewInt(7), big.NewInt(8), big.NewInt(9), |
|
big.NewInt(10), |
|
} |
|
|
|
var exp10cache [64]big.Int = func() [64]big.Int { |
|
e10, e10i := [64]big.Int{}, bigInt[1] |
|
for i := range e10 { |
|
e10[i].Set(e10i) |
|
e10i = new(big.Int).Mul(e10i, bigInt[10]) |
|
} |
|
return e10 |
|
}() |
|
|
|
// NewDec allocates and returns a new Dec set to the given int64 unscaled value |
|
// and scale. |
|
func NewDec(unscaled int64, scale Scale) *Dec { |
|
return new(Dec).SetUnscaled(unscaled).SetScale(scale) |
|
} |
|
|
|
// NewDecBig allocates and returns a new Dec set to the given *big.Int unscaled |
|
// value and scale. |
|
func NewDecBig(unscaled *big.Int, scale Scale) *Dec { |
|
return new(Dec).SetUnscaledBig(unscaled).SetScale(scale) |
|
} |
|
|
|
// Scale returns the scale of x. |
|
func (x *Dec) Scale() Scale { |
|
return x.scale |
|
} |
|
|
|
// Unscaled returns the unscaled value of x for u and true for ok when the |
|
// unscaled value can be represented as int64; otherwise it returns an undefined |
|
// int64 value for u and false for ok. Use x.UnscaledBig().Int64() to avoid |
|
// checking the validity of the value when the check is known to be redundant. |
|
func (x *Dec) Unscaled() (u int64, ok bool) { |
|
u = x.unscaled.Int64() |
|
var i big.Int |
|
ok = i.SetInt64(u).Cmp(&x.unscaled) == 0 |
|
return |
|
} |
|
|
|
// UnscaledBig returns the unscaled value of x as *big.Int. |
|
func (x *Dec) UnscaledBig() *big.Int { |
|
return &x.unscaled |
|
} |
|
|
|
// SetScale sets the scale of z, with the unscaled value unchanged, and returns |
|
// z. |
|
// The mathematical value of the Dec changes as if it was multiplied by |
|
// 10**(oldscale-scale). |
|
func (z *Dec) SetScale(scale Scale) *Dec { |
|
z.scale = scale |
|
return z |
|
} |
|
|
|
// SetUnscaled sets the unscaled value of z, with the scale unchanged, and |
|
// returns z. |
|
func (z *Dec) SetUnscaled(unscaled int64) *Dec { |
|
z.unscaled.SetInt64(unscaled) |
|
return z |
|
} |
|
|
|
// SetUnscaledBig sets the unscaled value of z, with the scale unchanged, and |
|
// returns z. |
|
func (z *Dec) SetUnscaledBig(unscaled *big.Int) *Dec { |
|
z.unscaled.Set(unscaled) |
|
return z |
|
} |
|
|
|
// Set sets z to the value of x and returns z. |
|
// It does nothing if z == x. |
|
func (z *Dec) Set(x *Dec) *Dec { |
|
if z != x { |
|
z.SetUnscaledBig(x.UnscaledBig()) |
|
z.SetScale(x.Scale()) |
|
} |
|
return z |
|
} |
|
|
|
// Sign returns: |
|
// |
|
// -1 if x < 0 |
|
// 0 if x == 0 |
|
// +1 if x > 0 |
|
// |
|
func (x *Dec) Sign() int { |
|
return x.UnscaledBig().Sign() |
|
} |
|
|
|
// Neg sets z to -x and returns z. |
|
func (z *Dec) Neg(x *Dec) *Dec { |
|
z.SetScale(x.Scale()) |
|
z.UnscaledBig().Neg(x.UnscaledBig()) |
|
return z |
|
} |
|
|
|
// Cmp compares x and y and returns: |
|
// |
|
// -1 if x < y |
|
// 0 if x == y |
|
// +1 if x > y |
|
// |
|
func (x *Dec) Cmp(y *Dec) int { |
|
xx, yy := upscale(x, y) |
|
return xx.UnscaledBig().Cmp(yy.UnscaledBig()) |
|
} |
|
|
|
// Abs sets z to |x| (the absolute value of x) and returns z. |
|
func (z *Dec) Abs(x *Dec) *Dec { |
|
z.SetScale(x.Scale()) |
|
z.UnscaledBig().Abs(x.UnscaledBig()) |
|
return z |
|
} |
|
|
|
// Add sets z to the sum x+y and returns z. |
|
// The scale of z is the greater of the scales of x and y. |
|
func (z *Dec) Add(x, y *Dec) *Dec { |
|
xx, yy := upscale(x, y) |
|
z.SetScale(xx.Scale()) |
|
z.UnscaledBig().Add(xx.UnscaledBig(), yy.UnscaledBig()) |
|
return z |
|
} |
|
|
|
// Sub sets z to the difference x-y and returns z. |
|
// The scale of z is the greater of the scales of x and y. |
|
func (z *Dec) Sub(x, y *Dec) *Dec { |
|
xx, yy := upscale(x, y) |
|
z.SetScale(xx.Scale()) |
|
z.UnscaledBig().Sub(xx.UnscaledBig(), yy.UnscaledBig()) |
|
return z |
|
} |
|
|
|
// Mul sets z to the product x*y and returns z. |
|
// The scale of z is the sum of the scales of x and y. |
|
func (z *Dec) Mul(x, y *Dec) *Dec { |
|
z.SetScale(x.Scale() + y.Scale()) |
|
z.UnscaledBig().Mul(x.UnscaledBig(), y.UnscaledBig()) |
|
return z |
|
} |
|
|
|
// Round sets z to the value of x rounded to Scale s using Rounder r, and |
|
// returns z. |
|
func (z *Dec) Round(x *Dec, s Scale, r Rounder) *Dec { |
|
return z.QuoRound(x, NewDec(1, 0), s, r) |
|
} |
|
|
|
// QuoRound sets z to the quotient x/y, rounded using the given Rounder to the |
|
// specified scale. |
|
// |
|
// If the rounder is RoundExact but the result can not be expressed exactly at |
|
// the specified scale, QuoRound returns nil, and the value of z is undefined. |
|
// |
|
// There is no corresponding Div method; the equivalent can be achieved through |
|
// the choice of Rounder used. |
|
// |
|
func (z *Dec) QuoRound(x, y *Dec, s Scale, r Rounder) *Dec { |
|
return z.quo(x, y, sclr{s}, r) |
|
} |
|
|
|
func (z *Dec) quo(x, y *Dec, s scaler, r Rounder) *Dec { |
|
scl := s.Scale(x, y) |
|
var zzz *Dec |
|
if r.UseRemainder() { |
|
zz, rA, rB := new(Dec).quoRem(x, y, scl, true, new(big.Int), new(big.Int)) |
|
zzz = r.Round(new(Dec), zz, rA, rB) |
|
} else { |
|
zz, _, _ := new(Dec).quoRem(x, y, scl, false, nil, nil) |
|
zzz = r.Round(new(Dec), zz, nil, nil) |
|
} |
|
if zzz == nil { |
|
return nil |
|
} |
|
return z.Set(zzz) |
|
} |
|
|
|
// QuoExact sets z to the quotient x/y and returns z when x/y is a finite |
|
// decimal. Otherwise it returns nil and the value of z is undefined. |
|
// |
|
// The scale of a non-nil result is "x.Scale() - y.Scale()" or greater; it is |
|
// calculated so that the remainder will be zero whenever x/y is a finite |
|
// decimal. |
|
func (z *Dec) QuoExact(x, y *Dec) *Dec { |
|
return z.quo(x, y, scaleQuoExact{}, RoundExact) |
|
} |
|
|
|
// quoRem sets z to the quotient x/y with the scale s, and if useRem is true, |
|
// it sets remNum and remDen to the numerator and denominator of the remainder. |
|
// It returns z, remNum and remDen. |
|
// |
|
// The remainder is normalized to the range -1 < r < 1 to simplify rounding; |
|
// that is, the results satisfy the following equation: |
|
// |
|
// x / y = z + (remNum/remDen) * 10**(-z.Scale()) |
|
// |
|
// See Rounder for more details about rounding. |
|
// |
|
func (z *Dec) quoRem(x, y *Dec, s Scale, useRem bool, |
|
remNum, remDen *big.Int) (*Dec, *big.Int, *big.Int) { |
|
// difference (required adjustment) compared to "canonical" result scale |
|
shift := s - (x.Scale() - y.Scale()) |
|
// pointers to adjusted unscaled dividend and divisor |
|
var ix, iy *big.Int |
|
switch { |
|
case shift > 0: |
|
// increased scale: decimal-shift dividend left |
|
ix = new(big.Int).Mul(x.UnscaledBig(), exp10(shift)) |
|
iy = y.UnscaledBig() |
|
case shift < 0: |
|
// decreased scale: decimal-shift divisor left |
|
ix = x.UnscaledBig() |
|
iy = new(big.Int).Mul(y.UnscaledBig(), exp10(-shift)) |
|
default: |
|
ix = x.UnscaledBig() |
|
iy = y.UnscaledBig() |
|
} |
|
// save a copy of iy in case it to be overwritten with the result |
|
iy2 := iy |
|
if iy == z.UnscaledBig() { |
|
iy2 = new(big.Int).Set(iy) |
|
} |
|
// set scale |
|
z.SetScale(s) |
|
// set unscaled |
|
if useRem { |
|
// Int division |
|
_, intr := z.UnscaledBig().QuoRem(ix, iy, new(big.Int)) |
|
// set remainder |
|
remNum.Set(intr) |
|
remDen.Set(iy2) |
|
} else { |
|
z.UnscaledBig().Quo(ix, iy) |
|
} |
|
return z, remNum, remDen |
|
} |
|
|
|
type sclr struct{ s Scale } |
|
|
|
func (s sclr) Scale(x, y *Dec) Scale { |
|
return s.s |
|
} |
|
|
|
type scaleQuoExact struct{} |
|
|
|
func (sqe scaleQuoExact) Scale(x, y *Dec) Scale { |
|
rem := new(big.Rat).SetFrac(x.UnscaledBig(), y.UnscaledBig()) |
|
f2, f5 := factor2(rem.Denom()), factor(rem.Denom(), bigInt[5]) |
|
var f10 Scale |
|
if f2 > f5 { |
|
f10 = Scale(f2) |
|
} else { |
|
f10 = Scale(f5) |
|
} |
|
return x.Scale() - y.Scale() + f10 |
|
} |
|
|
|
func factor(n *big.Int, p *big.Int) int { |
|
// could be improved for large factors |
|
d, f := n, 0 |
|
for { |
|
dd, dm := new(big.Int).DivMod(d, p, new(big.Int)) |
|
if dm.Sign() == 0 { |
|
f++ |
|
d = dd |
|
} else { |
|
break |
|
} |
|
} |
|
return f |
|
} |
|
|
|
func factor2(n *big.Int) int { |
|
// could be improved for large factors |
|
f := 0 |
|
for ; n.Bit(f) == 0; f++ { |
|
} |
|
return f |
|
} |
|
|
|
func upscale(a, b *Dec) (*Dec, *Dec) { |
|
if a.Scale() == b.Scale() { |
|
return a, b |
|
} |
|
if a.Scale() > b.Scale() { |
|
bb := b.rescale(a.Scale()) |
|
return a, bb |
|
} |
|
aa := a.rescale(b.Scale()) |
|
return aa, b |
|
} |
|
|
|
func exp10(x Scale) *big.Int { |
|
if int(x) < len(exp10cache) { |
|
return &exp10cache[int(x)] |
|
} |
|
return new(big.Int).Exp(bigInt[10], big.NewInt(int64(x)), nil) |
|
} |
|
|
|
func (x *Dec) rescale(newScale Scale) *Dec { |
|
shift := newScale - x.Scale() |
|
switch { |
|
case shift < 0: |
|
e := exp10(-shift) |
|
return NewDecBig(new(big.Int).Quo(x.UnscaledBig(), e), newScale) |
|
case shift > 0: |
|
e := exp10(shift) |
|
return NewDecBig(new(big.Int).Mul(x.UnscaledBig(), e), newScale) |
|
} |
|
return x |
|
} |
|
|
|
var zeros = []byte("00000000000000000000000000000000" + |
|
"00000000000000000000000000000000") |
|
var lzeros = Scale(len(zeros)) |
|
|
|
func appendZeros(s []byte, n Scale) []byte { |
|
for i := Scale(0); i < n; i += lzeros { |
|
if n > i+lzeros { |
|
s = append(s, zeros...) |
|
} else { |
|
s = append(s, zeros[0:n-i]...) |
|
} |
|
} |
|
return s |
|
} |
|
|
|
func (x *Dec) String() string { |
|
if x == nil { |
|
return "<nil>" |
|
} |
|
scale := x.Scale() |
|
s := []byte(x.UnscaledBig().String()) |
|
if scale <= 0 { |
|
if scale != 0 && x.unscaled.Sign() != 0 { |
|
s = appendZeros(s, -scale) |
|
} |
|
return string(s) |
|
} |
|
negbit := Scale(-((x.Sign() - 1) / 2)) |
|
// scale > 0 |
|
lens := Scale(len(s)) |
|
if lens-negbit <= scale { |
|
ss := make([]byte, 0, scale+2) |
|
if negbit == 1 { |
|
ss = append(ss, '-') |
|
} |
|
ss = append(ss, '0', '.') |
|
ss = appendZeros(ss, scale-lens+negbit) |
|
ss = append(ss, s[negbit:]...) |
|
return string(ss) |
|
} |
|
// lens > scale |
|
ss := make([]byte, 0, lens+1) |
|
ss = append(ss, s[:lens-scale]...) |
|
ss = append(ss, '.') |
|
ss = append(ss, s[lens-scale:]...) |
|
return string(ss) |
|
} |
|
|
|
// Format is a support routine for fmt.Formatter. It accepts the decimal |
|
// formats 'd' and 'f', and handles both equivalently. |
|
// Width, precision, flags and bases 2, 8, 16 are not supported. |
|
func (x *Dec) Format(s fmt.State, ch rune) { |
|
if ch != 'd' && ch != 'f' && ch != 'v' && ch != 's' { |
|
fmt.Fprintf(s, "%%!%c(dec.Dec=%s)", ch, x.String()) |
|
return |
|
} |
|
fmt.Fprintf(s, x.String()) |
|
} |
|
|
|
func (z *Dec) scan(r io.RuneScanner) (*Dec, error) { |
|
unscaled := make([]byte, 0, 256) // collects chars of unscaled as bytes |
|
dp, dg := -1, -1 // indexes of decimal point, first digit |
|
loop: |
|
for { |
|
ch, _, err := r.ReadRune() |
|
if err == io.EOF { |
|
break loop |
|
} |
|
if err != nil { |
|
return nil, err |
|
} |
|
switch { |
|
case ch == '+' || ch == '-': |
|
if len(unscaled) > 0 || dp >= 0 { // must be first character |
|
r.UnreadRune() |
|
break loop |
|
} |
|
case ch == '.': |
|
if dp >= 0 { |
|
r.UnreadRune() |
|
break loop |
|
} |
|
dp = len(unscaled) |
|
continue // don't add to unscaled |
|
case ch >= '0' && ch <= '9': |
|
if dg == -1 { |
|
dg = len(unscaled) |
|
} |
|
default: |
|
r.UnreadRune() |
|
break loop |
|
} |
|
unscaled = append(unscaled, byte(ch)) |
|
} |
|
if dg == -1 { |
|
return nil, fmt.Errorf("no digits read") |
|
} |
|
if dp >= 0 { |
|
z.SetScale(Scale(len(unscaled) - dp)) |
|
} else { |
|
z.SetScale(0) |
|
} |
|
_, ok := z.UnscaledBig().SetString(string(unscaled), 10) |
|
if !ok { |
|
return nil, fmt.Errorf("invalid decimal: %s", string(unscaled)) |
|
} |
|
return z, nil |
|
} |
|
|
|
// SetString sets z to the value of s, interpreted as a decimal (base 10), |
|
// and returns z and a boolean indicating success. The scale of z is the |
|
// number of digits after the decimal point (including any trailing 0s), |
|
// or 0 if there is no decimal point. If SetString fails, the value of z |
|
// is undefined but the returned value is nil. |
|
func (z *Dec) SetString(s string) (*Dec, bool) { |
|
r := strings.NewReader(s) |
|
_, err := z.scan(r) |
|
if err != nil { |
|
return nil, false |
|
} |
|
_, _, err = r.ReadRune() |
|
if err != io.EOF { |
|
return nil, false |
|
} |
|
// err == io.EOF => scan consumed all of s |
|
return z, true |
|
} |
|
|
|
// Scan is a support routine for fmt.Scanner; it sets z to the value of |
|
// the scanned number. It accepts the decimal formats 'd' and 'f', and |
|
// handles both equivalently. Bases 2, 8, 16 are not supported. |
|
// The scale of z is the number of digits after the decimal point |
|
// (including any trailing 0s), or 0 if there is no decimal point. |
|
func (z *Dec) Scan(s fmt.ScanState, ch rune) error { |
|
if ch != 'd' && ch != 'f' && ch != 's' && ch != 'v' { |
|
return fmt.Errorf("Dec.Scan: invalid verb '%c'", ch) |
|
} |
|
s.SkipSpace() |
|
_, err := z.scan(s) |
|
return err |
|
} |
|
|
|
// Gob encoding version |
|
const decGobVersion byte = 1 |
|
|
|
func scaleBytes(s Scale) []byte { |
|
buf := make([]byte, scaleSize) |
|
i := scaleSize |
|
for j := 0; j < scaleSize; j++ { |
|
i-- |
|
buf[i] = byte(s) |
|
s >>= 8 |
|
} |
|
return buf |
|
} |
|
|
|
func scale(b []byte) (s Scale) { |
|
for j := 0; j < scaleSize; j++ { |
|
s <<= 8 |
|
s |= Scale(b[j]) |
|
} |
|
return |
|
} |
|
|
|
// GobEncode implements the gob.GobEncoder interface. |
|
func (x *Dec) GobEncode() ([]byte, error) { |
|
buf, err := x.UnscaledBig().GobEncode() |
|
if err != nil { |
|
return nil, err |
|
} |
|
buf = append(append(buf, scaleBytes(x.Scale())...), decGobVersion) |
|
return buf, nil |
|
} |
|
|
|
// GobDecode implements the gob.GobDecoder interface. |
|
func (z *Dec) GobDecode(buf []byte) error { |
|
if len(buf) == 0 { |
|
return fmt.Errorf("Dec.GobDecode: no data") |
|
} |
|
b := buf[len(buf)-1] |
|
if b != decGobVersion { |
|
return fmt.Errorf("Dec.GobDecode: encoding version %d not supported", b) |
|
} |
|
l := len(buf) - scaleSize - 1 |
|
err := z.UnscaledBig().GobDecode(buf[:l]) |
|
if err != nil { |
|
return err |
|
} |
|
z.SetScale(scale(buf[l : l+scaleSize])) |
|
return nil |
|
} |
|
|
|
// MarshalText implements the encoding.TextMarshaler interface. |
|
func (x *Dec) MarshalText() ([]byte, error) { |
|
return []byte(x.String()), nil |
|
} |
|
|
|
// UnmarshalText implements the encoding.TextUnmarshaler interface. |
|
func (z *Dec) UnmarshalText(data []byte) error { |
|
_, ok := z.SetString(string(data)) |
|
if !ok { |
|
return fmt.Errorf("invalid inf.Dec") |
|
} |
|
return nil |
|
}
|
|
|