diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..598ae79 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.vscode diff --git a/arith_dec.go b/arith_dec.go new file mode 100644 index 0000000..3b4e815 --- /dev/null +++ b/arith_dec.go @@ -0,0 +1,55 @@ +package decimal + +import "math/bits" + +var pow10s = [...]uint64{ + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, + 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, + 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000, +} + +func pow10(n uint) uint { return uint(pow10s[n]) } + +var maxDigits = [...]uint{ + 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, + 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, + 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, + 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, +} + +// decDigits returns the minimum number of decimal digits required to represent x; +// the result is 0 for x == 0. +func decDigits(x uint) uint { + d := maxDigits[bits.Len(x)] + if x < pow10(d-1) { + d-- + } + return d +} + +// shr10VU sets z to x/(10**s) +func shr10VU(z, x dec, s uint) (r Word) { + d, m := Word(pow10(s)), Word(pow10(_WD-s)) + for i := len(x) - 1; i >= 0; i-- { + var q Word + rm := r * m + q, r = x[i]/d, x[i]%d + z[i] = rm + q + } + return r +} + +// The resulting carry c is either 0 or 1. +func add10VW(z, x []Word, y Word) (c Word) { + c = y + for i := 0; i < len(z) && i < len(x); i++ { + zi, cc := bits.Add(uint(x[i]), uint(c), 0) + if zi >= _BD { + zi -= _BD + c = 1 + } + z[i] = Word(zi) + c = Word(cc) + } + return +} diff --git a/dec.go b/dec.go new file mode 100644 index 0000000..9636c68 --- /dev/null +++ b/dec.go @@ -0,0 +1,189 @@ +package decimal + +import ( + "math/big" + "sync" +) + +const debugDecimal = true + +const ( + // _W * log10(2) = decimal digits per word. 9 decimal digits per 32 bits + // word and 19 per 64 bits word. + _WD = _W * 30103 / 100000 + // Decimal base for a word. 1e9 for 32 bits words and 1e19 for 64 bits + // words. TODO(db47h): We want this value to be a const. This is a dirty + // hack to avoid conditional compilation that will break if bits.UintSize>64 + _BD = 9999999998000000000*(_WD/19) + 1000000000*(_WD/9) +) + +// dec is an unsigned integer x of the form +// +// x = x[n-1]*_BD^(n-1) + x[n-2]*_BD^(n-2) + ... + x[1]*_BD + x[0] +// +// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n, +// with the digits x[i] as the slice elements. +// +// A number is normalized if the slice contains no leading 0 digits. +// During arithmetic operations, denormalized values may occur but are +// always normalized before returning the final result. The normalized +// representation of 0 is the empty or nil slice (length = 0). +type dec []Word + +// norm normalizes dec z by multiplying it by 10 until the most significant +// digit is >= 1. +// +// Returns z with trailing zeros truncated and right shifted (in 10 base) such +// that z % 10 = 0. Returns z and the shift amount. +func (z dec) norm() (dec, uint) { + // truncate leading zero words + z = z[:z.mszw()] + if len(z) == 0 { + return z, 0 + } + + // find lowest non-zero word + exp := uint(0) + i := 0 + w := Word(0) + for i, w = range z { + if w != 0 { + break + } + exp += _WD + } + if debugDecimal && i == len(z) { + panic("BUG: no non-zero word found") + } + + // truncate + if i > 0 { + copy(z, z[i:]) + z = z[:len(z)-i] + } + + // partial shift + e := uint(0) + for x := w; x%10 == 0; x /= 10 { + e++ + } + if e != 0 { + exp += e + r := shr10VU(z, z, e) + if z[len(z)-1] == 0 { + z = z[:len(z)-1] + } + if debugDecimal && r != 0 { + panic("remainder != 0 after norm") + } + } + return z, exp +} + +// shr10 shifts z right by s decimal places. Returns +// z and the most significant digit removed and a boolean +// indicating if there were any non-zero digits following r +func (z dec) shr10(s uint) (d dec, r Word, tnz bool) { + nw, s := s/_WD, s%_WD + if nw > 0 { + // save rounding digit + r = z[nw-1] + for _, w := range z[:nw-1] { + tnz = tnz || w != 0 + } + copy(z, z[nw:]) + z = z[:len(z)-int(nw)] + } + if s == 0 { + r, m := r/(_BD-1), r%(_BD-1) + return z, r, m != 0 + } + tnz = tnz || r != 0 + // shift right by 0 < s < _WD + r = shr10VU(z, z, s) + p := Word(pow10(s - 1)) + r, m := r/p, r%p + return z, r, tnz || m != 0 +} + +func (x dec) digit(n uint) uint { + n, m := n/_WD, n%_WD + return (uint(x[n]) / pow10(m)) % 10 +} + +// mszw returns the index of the most significant zero-word +// such that x === x[:x.mszw()]. +func (x dec) mszw() uint { + i := uint(len(x)) + for i != 0 && x[i-1] == 0 { + i-- + } + if i == 0 { + return uint(len(x)) + } + return i +} + +func (x dec) digits() uint { + for msw := len(x) - 1; msw >= 0; msw-- { + if x[msw] != 0 { + return uint(msw)*_WD + decDigits(uint(x[msw])) + } + } + return 0 +} + +func (z dec) set(x dec) dec { + z = z.make(len(x)) + copy(z, x) + return z +} + +func (z dec) make(n int) dec { + if n <= cap(z) { + return z[:n] // reuse z + } + if n == 1 { + // Most decs start small and stay that way; don't over-allocate. + return make(dec, 1) + } + // Choosing a good value for e has significant performance impact + // because it increases the chance that a value can be reused. + const e = 4 // extra capacity + return make(dec, n, n+e) +} + +func (z dec) setInt(x *big.Int) dec { + b := new(big.Int).Set(x).Bits() + n := len(b) + i := 0 + for ; n > 0; i++ { + z[i] = Word(divWVW_g(b, 0, b, big.Word(_BD))) + n = len(b) + for n > 0 && b[n-1] == 0 { + n-- + } + b = b[0:n] + } + return z[:i] +} + +// getDec returns a *dec of len n. The contents may not be zero. +// The pool holds *dec to avoid allocation when converting to interface{}. +func getDec(n int) *dec { + var z *dec + if v := decPool.Get(); v != nil { + z = v.(*dec) + } + if z == nil { + z = new(dec) + } + *z = z.make(n) + return z +} + +func putDec(x *dec) { + decPool.Put(x) +} + +var decPool sync.Pool diff --git a/dec_test.go b/dec_test.go new file mode 100644 index 0000000..a5af1ab --- /dev/null +++ b/dec_test.go @@ -0,0 +1,86 @@ +package decimal + +import ( + "math/big" + "math/bits" + "math/rand" + "testing" + "time" +) + +func Test_decNorm(t *testing.T) { + rand.Seed(time.Now().UnixNano()) + for i := 0; i < 10000; i++ { + again: + w := uint(rand.Uint64()) % _BD + if w%10 == 0 { + goto again + } + e := uint(rand.Intn(_WD + 1)) + h, l := bits.Mul(w, pow10(e)) + // convert h, l from base _B (2**64) to base _BD (10**19) or 2**32 -> 10**9 + h, l = bits.Div(h, l, _BD) + d, s := dec{Word(l), Word(h)}.norm() + if len(d) > 1 || d[0] != Word(w) || s != e { + t.Fatalf("%ve%v => dec{%v, %v}.norm() = %v, %v --- Expected [%d], %d", + w, e, l, h, d, s, w, e) + } + } +} + +func Test_decDigits(t *testing.T) { + rand.Seed(time.Now().UnixNano()) + for i := 0; i < 10000; i++ { + n := uint(rand.Uint64()) + d := uint(0) + for m := n; m != 0; m /= 10 { + d++ + } + if dd := decDigits(n); dd != d { + t.Fatalf("decDigits(%d) = %d, expected %d", n, dd, d) + } + } +} + +func Test_decShr10(t *testing.T) { + d := dec{1234, 0, 1234567890123456789} + d, r, tnz := d.shr10(20) + t.Log(d, r, tnz) +} + +func Test_decSetInt(t *testing.T) { + b, _ := new(big.Int).SetString("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", 0) + prec := uint32(float64(b.BitLen())/ln2_10) + 1 + d := dec{}.make((int(prec) + _WD - 1) / _WD) + d = d.setInt(b) + t.Log(d, len(d)) +} + +var ( + benchD dec + benchU uint +) + +func Benchmark_decNorm(b *testing.B) { + rand.Seed(0xdeadbeefbadf00d) + for i := 0; i < b.N; i++ { + w := uint(rand.Uint64()) % _BD + e := uint(rand.Intn(_WD)) + h, l := w/pow10(_WD-e), (w%pow10(_WD-e))*pow10(e) + benchD, benchU = dec{Word(l), Word(h)}.norm() + } +} + +func Benchmark_decDigits(b *testing.B) { + rand.Seed(0xdeadbeefbadf00d) + for i := 0; i < b.N; i++ { + benchU = decDigits(uint(rand.Uint64()) % _BD) + } +} + +func BenchmarkDecDigits(b *testing.B) { + rand.Seed(0xdeadbeefbadf00d) + for i := 0; i < b.N; i++ { + benchU = dec{Word(rand.Uint64()) % _BD}.digits() + } +} diff --git a/decconv.go b/decconv.go new file mode 100644 index 0000000..cda28ef --- /dev/null +++ b/decconv.go @@ -0,0 +1,123 @@ +package decimal + +import ( + "fmt" + "io" + "strings" +) + +var decimalZero Decimal + +// SetString sets z to the value of s and returns z and a boolean indicating +// success. s must be a floating-point number of the same format as accepted +// by Parse, with base argument 0. The entire string (not just a prefix) must +// be valid for success. If the operation failed, the value of z is undefined +// but the returned value is nil. +func (z *Decimal) SetString(s string) (*Decimal, bool) { + if f, _, err := z.Parse(s, 0); err == nil { + return f, true + } + return nil, false +} + +// scan is like Parse but reads the longest possible prefix representing a valid +// floating point number from an io.ByteScanner rather than a string. It serves +// as the implementation of Parse. It does not recognize ±Inf and does not expect +// EOF at the end. +func (z *Decimal) scan(r io.ByteScanner, base int) (f *Decimal, b int, err error) { + panic("not implemented") +} + +// Parse parses s which must contain a text representation of a floating- point +// number with a mantissa in the given conversion base (the exponent is always a +// decimal number), or a string representing an infinite value. +// +// For base 0, an underscore character ``_'' may appear between a base prefix +// and an adjacent digit, and between successive digits; such underscores do not +// change the value of the number, or the returned digit count. Incorrect +// placement of underscores is reported as an error if there are no other +// errors. If base != 0, underscores are not recognized and thus terminate +// scanning like any other character that is not a valid radix point or digit. +// +// It sets z to the (possibly rounded) value of the corresponding floating- +// point value, and returns z, the actual base b, and an error err, if any. The +// entire string (not just a prefix) must be consumed for success. If z's +// precision is 0, it is changed to fit all digits of the mantissa before +// rounding takes effect. The number must be of the form: +// +// number = [ sign ] ( float | "inf" | "Inf" ) . +// sign = "+" | "-" . +// float = ( mantissa | prefix pmantissa ) [ exponent ] . +// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] . +// mantissa = digits "." [ digits ] | digits | "." digits . +// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits . +// exponent = ( "e" | "E" | "p" | "P" ) [ sign ] digits . +// digits = digit { [ "_" ] digit } . +// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . +// +// The base argument must be 0, 2, 8, 10, or 16. Providing an invalid base +// argument will lead to a run-time panic. +// +// For base 0, the number prefix determines the actual base: A prefix of ``0b'' +// or ``0B'' selects base 2, ``0o'' or ``0O'' selects base 8, and ``0x'' or +// ``0X'' selects base 16. Otherwise, the actual base is 10 and no prefix is +// accepted. The octal prefix "0" is not supported (a leading "0" is simply +// considered a "0"). +// +// A "p" or "P" exponent indicates a base 2 (rather then base 10) exponent; for +// instance, "0x1.fffffffffffffp1023" (using base 0) represents the maximum +// float64 value. For hexadecimal mantissae, the exponent character must be one +// of 'p' or 'P', if present (an "e" or "E" exponent indicator cannot be +// distinguished from a mantissa digit). +// +// Note that rounding only happens if z's precision is not zero and less than +// the number of digits in the mantissa or with a base 2 exponent, in which case +// it is best to use ParseFloat then z.SetFloat. +// +// The returned *Decimal f is nil and the value of z is valid but not defined if +// an error is reported. +// +func (z *Decimal) Parse(s string, base int) (f *Decimal, b int, err error) { + // scan doesn't handle ±Inf + if len(s) == 3 && (s == "Inf" || s == "inf") { + f = z.SetInf(false) + return + } + if len(s) == 4 && (s[0] == '+' || s[0] == '-') && (s[1:] == "Inf" || s[1:] == "inf") { + f = z.SetInf(s[0] == '-') + return + } + + r := strings.NewReader(s) + if f, b, err = z.scan(r, base); err != nil { + return + } + + // entire string must have been consumed + if ch, err2 := r.ReadByte(); err2 == nil { + err = fmt.Errorf("expected end of string, found %q", ch) + } else if err2 != io.EOF { + err = err2 + } + + return +} + +// ParseDecimal is like f.Parse(s, base) with f set to the given precision +// and rounding mode. +func ParseDecimal(s string, base int, prec uint, mode RoundingMode) (f *Decimal, b int, err error) { + return new(Decimal).SetPrec(prec).SetMode(mode).Parse(s, base) +} + +var _ fmt.Scanner = &decimalZero // *Decimal must implement fmt.Scanner + +// Scan is a support routine for fmt.Scanner; it sets z to the value of +// the scanned number. It accepts formats whose verbs are supported by +// fmt.Scan for floating point values, which are: +// 'b' (binary), 'e', 'E', 'f', 'F', 'g' and 'G'. +// Scan doesn't handle ±Inf. +func (z *Decimal) Scan(s fmt.ScanState, ch rune) error { + s.SkipSpace() + _, _, err := z.scan(byteReader{s}, 0) + return err +} diff --git a/decimal.go b/decimal.go new file mode 100644 index 0000000..e8aafc6 --- /dev/null +++ b/decimal.go @@ -0,0 +1,394 @@ +package decimal + +import ( + "fmt" + "math" + "math/big" +) + +type Decimal struct { + mant dec + exp int32 + prec uint32 + dig uint32 + mode RoundingMode + acc Accuracy + form form + neg bool +} + +func NewDecimal(x float64) *Decimal { + panic("not implemented") +} + +func (z *Decimal) Abs(x *Decimal) *Decimal { + panic("not implemented") +} + +// Acc returns the accuracy of x produced by the most recent operation. +func (x *Decimal) Acc() Accuracy { + return x.acc +} + +func (z *Decimal) Add(x, y *Decimal) *Decimal { + panic("not implemented") +} + +func (x *Decimal) Append(buf []byte, fmt byte, prec int) []byte { + panic("not implemented") +} + +func (x *Decimal) Cmp(y *Decimal) int { + panic("not implemented") +} + +func (z *Decimal) Copy(x *Decimal) *Decimal { + panic("not implemented") +} + +func (x *Decimal) Float32() (float32, Accuracy) { + panic("not implemented") +} + +func (x *Decimal) Float64() (float64, Accuracy) { + panic("not implemented") +} + +func (x *Decimal) Format(s fmt.State, format rune) { + panic("not implemented") +} + +func (z *Decimal) GobDecode(buf []byte) error { + panic("not implemented") +} + +func (x *Decimal) GobEncode() ([]byte, error) { + panic("not implemented") +} + +func (x *Decimal) Int(z *big.Int) (*big.Int, Accuracy) { + panic("not implemented") +} + +func (x *Decimal) Int64() (int64, Accuracy) { + panic("not implemented") +} + +// IsInf reports whether x is +Inf or -Inf. +func (x *Decimal) IsInf() bool { + return x.form == inf +} + +func (x *Decimal) IsInt() bool { + panic("not implemented") +} + +func (x *Decimal) MantExp(mant *Decimal) (exp int) { + panic("not implemented") +} + +func (x *Decimal) MarshalText() (text []byte, err error) { + panic("not implemented") +} + +// MinPrec returns the minimum precision required to represent x exactly +// (i.e., the smallest prec before x.SetPrec(prec) would start rounding x). +// The result is 0 for |x| == 0 and |x| == Inf. +func (x *Decimal) MinPrec() uint { + if x.form != finite { + return 0 + } + return uint(x.dig) +} + +// Mode returns the rounding mode of x. +func (x *Decimal) Mode() RoundingMode { + return x.mode +} + +func (z *Decimal) Mul(x, y *Decimal) *Decimal { + panic("not implemented") +} + +// Neg sets z to the (possibly rounded) value of x with its sign negated, +// and returns z. +func (z *Decimal) Neg(x *Decimal) *Decimal { + z.Set(x) + z.neg = !z.neg + return z +} + +// Prec returns the mantissa precision of x in bits. +// The result may be 0 for |x| == 0 and |x| == Inf. +func (x *Decimal) Prec() uint { + return uint(x.prec) +} + +func (z *Decimal) Quo(x, y *Decimal) *Decimal { + panic("not implemented") +} + +func (x *Decimal) Rat(z *big.Rat) (*big.Rat, Accuracy) { + panic("not implemented") +} + +// Set sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the precision of x +// before setting z (and rounding will have no effect). +// Rounding is performed according to z's precision and rounding +// mode; and z's accuracy reports the result error relative to the +// exact (not rounded) result. +func (z *Decimal) Set(x *Decimal) *Decimal { + if debugDecimal { + x.validate() + } + z.acc = Exact + if z != x { + z.form = x.form + z.neg = x.neg + z.dig = x.dig + if x.form == finite { + z.exp = x.exp + z.mant = z.mant.set(x.mant) + } + if z.prec == 0 { + z.prec = x.prec + } else if z.prec < x.prec { + z.round() + } + } + return z +} + +func (z *Decimal) SetFloat64(x float64) *Decimal { + panic("not implemented") +} + +// SetInf sets z to the infinite Decimal -Inf if signbit is +// set, or +Inf if signbit is not set, and returns z. The +// precision of z is unchanged and the result is always +// Exact. +func (z *Decimal) SetInf(signbit bool) *Decimal { + z.acc = Exact + z.form = inf + z.neg = signbit + return z +} + +const ln2_10 = math.Ln10 / math.Ln2 + +// SetInt sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the larger of x.BitLen() +// or 64 (and rounding will have no effect). +func (z *Decimal) SetInt(x *big.Int) *Decimal { + bits := uint32(x.BitLen()) + // estimate precision. May overshoot actual precision by 1. + prec := uint32(float64(bits)/ln2_10) + 1 + if z.prec == 0 { + z.prec = umax32(prec, _WD) + } + // TODO(db47h) truncating x could be more efficient if z.prec > 0 + // but small compared to the size of x, or if there + // are many trailing 0's. + z.acc = Exact + z.neg = x.Sign() < 0 + if bits == 0 { + z.form = zero + return z + } + // x != 0 + exp := uint(0) + z.mant = z.mant.make((int(prec) + _WD - 1) / _WD).setInt(x) + z.mant, exp = z.mant.norm() + z.dig = uint32(z.mant.digits()) + z.setExpAndRound(int64(exp) + int64(z.dig)) + return z +} + +func (z *Decimal) SetInt64(x int64) *Decimal { + panic("not implemented") +} + +func (z *Decimal) setExpAndRound(exp int64) { + if exp < MinExp { + // underflow + z.acc = makeAcc(z.neg) + z.form = zero + return + } + + if exp > MaxExp { + // overflow + z.acc = makeAcc(!z.neg) + z.form = inf + return + } + + z.form = finite + z.exp = int32(exp) + z.round() +} + +func (z *Decimal) SetMantExp(mant *Decimal, exp int) *Decimal { + panic("not implemented") +} + +// SetMode sets z's rounding mode to mode and returns an exact z. +// z remains unchanged otherwise. +// z.SetMode(z.Mode()) is a cheap way to set z's accuracy to Exact. +func (z *Decimal) SetMode(mode RoundingMode) *Decimal { + z.mode = mode + z.acc = Exact + return z +} + +func (z *Decimal) SetPrec(prec uint) *Decimal { + panic("not implemented") +} + +func (z *Decimal) SetRat(x *big.Rat) *Decimal { + panic("not implemented") +} + +func (z *Decimal) SetUint64(x uint64) *Decimal { + panic("not implemented") +} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x is ±0 +// +1 if x > 0 +// +func (x *Decimal) Sign() int { + if debugDecimal { + x.validate() + } + if x.form == zero { + return 0 + } + if x.neg { + return -1 + } + return 1 +} + +// Signbit reports whether x is negative or negative zero. +func (x *Decimal) Signbit() bool { + return x.neg +} + +func (z *Decimal) Sqrt(x *Decimal) *Decimal { + panic("not implemented") +} + +func (x *Decimal) String() string { + panic("not implemented") +} + +func (z *Decimal) Sub(x, y *Decimal) *Decimal { + panic("not implemented") +} + +func (x *Decimal) Text(format byte, prec int) string { + panic("not implemented") +} + +func (x *Decimal) Uint64() (uint64, Accuracy) { + panic("not implemented") +} + +func (z *Decimal) UnmarshalText(text []byte) error { + panic("not implemented") +} + +func (x *Decimal) validate() { + if !debugDecimal { + // avoid performance bugs + panic("validate called but debugDecimal is not set") + } + if x.form != finite { + return + } + m := len(x.mant) + if m == 0 { + panic("nonzero finite number with empty mantissa") + } + if x.mant[m-1] == 0 { + panic(fmt.Sprintf("last word of %s is zero", x.Text('e', 0))) + } + if x.mant[0]%10 == 0 { + panic(fmt.Sprintf("first word %d of %s is divisible by 10", x.mant[0], x.Text('e', 0))) + } + if d := uint32(x.mant.digits()); x.dig != d { + panic(fmt.Sprintf("digit count %d != real digit count %d for %s", x.dig, d, x.Text('e', 0))) + } + if x.prec == 0 { + panic("zero precision finite number") + } + +} + +// round rounds z according to z.mode to z.prec digits and sets z.acc accordingly. +// z's mantissa must be normalized or empty. +// +// CAUTION: The rounding modes ToNegativeInf, ToPositiveInf are affected by the +// sign of z. For correct rounding, the sign of z must be set correctly before +// calling round. +func (z *Decimal) round() { + var sbit bool + if debugDecimal { + z.validate() + } + + z.acc = Exact + if z.form != finite { + // ±0 or ±Inf => nothing left to do + return + } + // z.form == finite && len(z.mant) > 0 + // m > 0 implies z.prec > 0 (checked by validate) + // m := uint32(len(z.mant)) // present mantissa length in words + if z.dig <= z.prec { + // mantissa fits => nothing to do + return + } + + // digits > z.prec + // r := uint(z.digits - z.prec - 1) + // rd := z.mant.digit(r) + + var r Word + z.mant, r, sbit = z.mant.shr10(uint(z.dig - z.prec)) + z.dig = z.prec + + if r != 0 || sbit { + inc := false + switch z.mode { + case ToNegativeInf: + inc = z.neg + case ToZero: + // nothing to do + case ToNearestEven: + inc = r > 5 || (r == 5 && (sbit || z.mant[0]&1 != 0)) + case ToNearestAway: + inc = r >= 5 + case AwayFromZero: + inc = true + case ToPositiveInf: + inc = !z.neg + default: + panic("unreachable") + } + z.acc = makeAcc(inc != z.neg) + if inc { + // add 1 to mantissa + if add10VW(z.mant, z.mant, 1) != 0 { + + } + } + } + if debugDecimal { + z.validate() + } +} diff --git a/decimal_test.go b/decimal_test.go new file mode 100644 index 0000000..d10278d --- /dev/null +++ b/decimal_test.go @@ -0,0 +1,13 @@ +package decimal + +import ( + "math/big" + "testing" +) + +func TestDecimal_SetInt(t *testing.T) { + b, _ := new(big.Int).SetString("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", 0) + d := new(Decimal) + d.SetInt(b) + t.Log(d.prec, d.dig, d.exp) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8e60a82 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/db47h/decimal + +go 1.14 diff --git a/stdlib.go b/stdlib.go new file mode 100644 index 0000000..f824083 --- /dev/null +++ b/stdlib.go @@ -0,0 +1,128 @@ +// This file mirrors types and constants from math/big. + +package decimal + +import ( + "fmt" + "math" + "math/big" + "math/bits" +) + +// MaxBase is the largest number base accepted for string conversions. +const MaxBase = 10 + ('z' - 'a' + 1) + ('Z' - 'A' + 1) + +// Exponent and precision limits. +const ( + MaxExp = math.MaxInt32 // largest supported exponent + MinExp = math.MinInt32 // smallest supported exponent + MaxPrec = math.MaxUint32 // largest (theoretically) supported precision; likely memory-limited +) + +// Internal representation: The mantissa bits x.mant of a nonzero finite +// Decimal x are stored in a dec slice long enough to hold up to x.prec digits; +// +// A zero or non-finite Decimal x ignores x.mant and x.exp. +// +// x form neg mant exp +// ---------------------------------------------------------- +// ±0 zero sign - - +// 0 < |x| < +Inf finite sign mantissa exponent +// ±Inf inf sign - - + +// A form value describes the internal representation. +type form byte + +// The form value order is relevant - do not change! +const ( + zero form = iota + finite + inf +) + +// RoundingMode determines how a Float value is rounded to the +// desired precision. Rounding may change the Float value; the +// rounding error is described by the Float's Accuracy. +type RoundingMode byte + +// These constants define supported rounding modes. +const ( + ToNearestEven RoundingMode = iota // == IEEE 754-2008 roundTiesToEven + ToNearestAway // == IEEE 754-2008 roundTiesToAway + ToZero // == IEEE 754-2008 roundTowardZero + AwayFromZero // no IEEE 754-2008 equivalent + ToNegativeInf // == IEEE 754-2008 roundTowardNegative + ToPositiveInf // == IEEE 754-2008 roundTowardPositive +) + +//go:generate stringer -type=RoundingMode + +// Accuracy describes the rounding error produced by the most recent +// operation that generated a Float value, relative to the exact value. +type Accuracy int8 + +// Constants describing the Accuracy of a Float. +const ( + Below Accuracy = -1 + Exact Accuracy = 0 + Above Accuracy = +1 +) + +//go:generate stringer -type=Accuracy + +func makeAcc(above bool) Accuracy { + if above { + return Above + } + return Below +} + +// A Word represents a single digit of a multi-precision unsigned integer. +type Word uint + +const ( + // _S = _W / 8 // word size in bytes + + _W = bits.UintSize // word size in bits + // _B = 1 << _W // digit base + // _M = _B - 1 // digit mask +) + +// byteReader is a local wrapper around fmt.ScanState; +// it implements the ByteReader interface. +type byteReader struct { + fmt.ScanState +} + +func (r byteReader) ReadByte() (byte, error) { + ch, size, err := r.ReadRune() + if size != 1 && err == nil { + err = fmt.Errorf("invalid rune %#U", ch) + } + return byte(ch), err +} + +func (r byteReader) UnreadByte() error { + return r.UnreadRune() +} + +func umax32(x, y uint32) uint32 { + if x > y { + return x + } + return y +} + +// q = (u1<<_W + u0 - r)/v +func divWW_g(u1, u0, v big.Word) (q, r big.Word) { + qq, rr := bits.Div(uint(u1), uint(u0), uint(v)) + return big.Word(qq), big.Word(rr) +} + +func divWVW_g(z []big.Word, xn big.Word, x []big.Word, y big.Word) (r big.Word) { + r = xn + for i := len(z) - 1; i >= 0; i-- { + z[i], r = divWW_g(r, x[i], y) + } + return r +}