Browse Source

initial import

speter 13 years ago
commit
4e6f4a0459
6 changed files with 1652 additions and 0 deletions
  1. 57 0
      LICENSE
  2. 10 0
      README
  3. 210 0
      dec/benchmark_test.go
  4. 859 0
      dec/dec.go
  5. 454 0
      dec/dec_test.go
  6. 62 0
      dec/example_test.go

+ 57 - 0
LICENSE

@@ -0,0 +1,57 @@
+Copyright (c) 2012 Peter Suranyi. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+Portions of godec's source code have been derived from Go, and are 
+covered by the following license:
+----------------------------------------------------------------------
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 10 - 0
README

@@ -0,0 +1,10 @@
+godec: multi-precision decimal arithmetic for go
+
+The API and the implementation are based on and complement those in the
+multi-precision integer (Int) implementation in the Go library (math/big).
+
+Limitations:
+Support for formatting options is currently missing.
+There is no support for potentially lossy conversions (e.g. float64 to Dec,
+Dec to int64, or Dec to float64) as the requirements for these usually differ
+depending on the use case.

+ 210 - 0
dec/benchmark_test.go

@@ -0,0 +1,210 @@
+package dec
+
+import (
+	"fmt"
+	"math/big"
+	"math/rand"
+	"sync"
+	"testing"
+)
+
+const maxcap = 1024 * 1024
+const bits = 256
+const maxscale = 32
+
+var once sync.Once
+
+var decInput [][2]Dec
+var intInput [][2]big.Int
+
+var initBench = func() {
+	decInput = make([][2]Dec, maxcap)
+	intInput = make([][2]big.Int, maxcap)
+	max := new(big.Int).Lsh(big.NewInt(1), bits)
+	r := rand.New(rand.NewSource(0))
+	for i := 0; i < cap(decInput); i++ {
+		decInput[i][0].SetUnscaled(new(big.Int).Rand(r, max)).
+			SetScale(Scale(r.Int31n(int32(2*maxscale-1)) - int32(maxscale)))
+		decInput[i][1].SetUnscaled(new(big.Int).Rand(r, max)).
+			SetScale(Scale(r.Int31n(int32(2*maxscale-1)) - int32(maxscale)))
+	}
+	for i := 0; i < cap(intInput); i++ {
+		intInput[i][0].Rand(r, max)
+		intInput[i][1].Rand(r, max)
+	}
+}
+
+func doBenchmarkDec1(b *testing.B, f func(z *Dec)) {
+	once.Do(initBench)
+	b.ResetTimer()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		f(&decInput[i%maxcap][0])
+	}
+}
+
+func doBenchmarkDec2(b *testing.B, f func(x, y *Dec)) {
+	once.Do(initBench)
+	b.ResetTimer()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		f(&decInput[i%maxcap][0], &decInput[i%maxcap][1])
+	}
+}
+
+func doBenchmarkInt1(b *testing.B, f func(z *big.Int)) {
+	once.Do(initBench)
+	b.ResetTimer()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		f(&intInput[i%maxcap][0])
+	}
+}
+
+func doBenchmarkInt2(b *testing.B, f func(x, y *big.Int)) {
+	once.Do(initBench)
+	b.ResetTimer()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		f(&intInput[i%maxcap][0], &intInput[i%maxcap][1])
+	}
+}
+
+func Benchmark_Dec_String(b *testing.B) {
+	doBenchmarkDec1(b, func(x *Dec) {
+		x.String()
+	})
+}
+
+func Benchmark_Dec_StringScan(b *testing.B) {
+	doBenchmarkDec1(b, func(x *Dec) {
+		s := x.String()
+		d := new(Dec)
+		fmt.Sscan(s, d)
+	})
+}
+
+func Benchmark_Dec_GobEncode(b *testing.B) {
+	doBenchmarkDec1(b, func(x *Dec) {
+		x.GobEncode()
+	})
+}
+
+func Benchmark_Dec_GobEnDecode(b *testing.B) {
+	doBenchmarkDec1(b, func(x *Dec) {
+		g, _ := x.GobEncode()
+		new(Dec).GobDecode(g)
+	})
+}
+
+func Benchmark_Dec_Add(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		ys := y.Scale()
+		y.SetScale(x.Scale())
+		_ = new(Dec).Add(x, y)
+		y.SetScale(ys)
+	})
+}
+
+func Benchmark_Dec_AddMixed(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).Add(x, y)
+	})
+}
+
+func Benchmark_Dec_Sub(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		ys := y.Scale()
+		y.SetScale(x.Scale())
+		_ = new(Dec).Sub(x, y)
+		y.SetScale(ys)
+	})
+}
+
+func Benchmark_Dec_SubMixed(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).Sub(x, y)
+	})
+}
+
+func Benchmark_Dec_Mul(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).Mul(x, y)
+	})
+}
+
+func Benchmark_Dec_Mul_QuoExact(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		v := new(Dec).Mul(x, y)
+		_ = new(Dec).QuoExact(v, y)
+	})
+}
+
+func Benchmark_Dec_Quo_Fixed_Down(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).Quo(x, y, ScaleFixed0, RoundDown)
+	})
+}
+
+func Benchmark_Dec_Quo_Fixed_HalfUp(b *testing.B) {
+	doBenchmarkDec2(b, func(x, y *Dec) {
+		_ = new(Dec).Quo(x, y, ScaleFixed0, RoundHalfUp)
+	})
+}
+
+func Benchmark_Int_String(b *testing.B) {
+	doBenchmarkInt1(b, func(x *big.Int) {
+		x.String()
+	})
+}
+
+func Benchmark_Int_StringScan(b *testing.B) {
+	doBenchmarkInt1(b, func(x *big.Int) {
+		s := x.String()
+		d := new(big.Int)
+		fmt.Sscan(s, d)
+	})
+}
+
+func Benchmark_Int_GobEncode(b *testing.B) {
+	doBenchmarkInt1(b, func(x *big.Int) {
+		x.GobEncode()
+	})
+}
+
+func Benchmark_Int_GobEnDecode(b *testing.B) {
+	doBenchmarkInt1(b, func(x *big.Int) {
+		g, _ := x.GobEncode()
+		new(big.Int).GobDecode(g)
+	})
+}
+
+func Benchmark_Int_Add(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_ = new(big.Int).Add(x, y)
+	})
+}
+
+func Benchmark_Int_Sub(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_ = new(big.Int).Sub(x, y)
+	})
+}
+
+func Benchmark_Int_Mul(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_ = new(big.Int).Mul(x, y)
+	})
+}
+
+func Benchmark_Int_Quo(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_ = new(big.Int).Quo(x, y)
+	})
+}
+
+func Benchmark_Int_QuoRem(b *testing.B) {
+	doBenchmarkInt2(b, func(x, y *big.Int) {
+		_, _ = new(big.Int).QuoRem(x, y, new(big.Int))
+	})
+}

+ 859 - 0
dec/dec.go

@@ -0,0 +1,859 @@
+// Package dec implements multi-precision decimal arithmetic.
+// It supports the numeric type Dec for signed decimals.
+// It is based on and complements the multi-precision integer implementation
+// (Int) in the Go library (math/big).
+//
+// 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.
+//
+// Quotient (division) operation uses Scalers and Rounders to specify the
+// desired behavior. See Quo, Scaler, and Rounder for details. 
+//
+package dec
+
+// This file implements signed multi-precision decimals.
+
+import (
+	"fmt"
+	"io"
+	"math/big"
+	"strings"
+)
+
+// A Dec represents a signed multi-precision decimal.
+// It is stored as a combination of a multi-precision big.Int unscaled value
+// and a fixed-precision scale of type 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.
+//
+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 function that returns the scale for the result of an
+// operation on x and y.
+type Scaler func(x *Dec, y *Dec) (scale Scale)
+
+// Rounder represents a method for rounding the (possibly infinite decimal)
+// result of a division to a finite Dec. It is used by Quo().
+//
+type Rounder interface {
+
+	// When UseRemainder() returns true, the Round() method is passed the
+	// remainder of the division, expressed as the numerator and denominator of
+	// a rational.
+	UseRemainder() bool
+
+	// Round sets the rounded value of a quotient to z, and returns z.
+	// quo is rounded down (truncated towards zero) to the scale obtained from
+	// the Scaler in Quo().
+	//
+	// When the remainder is not used, remNum and remDen are nil.
+	// When used, the remainder is normalized between -1 and 1; that is:
+	// 
+	//  -|remDen| < remNum < |remDen|
+	//
+	// remDen has the same sign as y, and remNum is zero or has the same sign
+	// as x.
+	Round(z, quo *Dec, remNum, remDen *big.Int) *Dec
+}
+
+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 unscaled value and
+// scale.
+func NewDec(unscaled *big.Int, scale Scale) *Dec {
+	return new(Dec).SetUnscaled(unscaled).SetScale(scale)
+}
+
+// NewDecInt64 allocates and returns a new Dec set to the given int64 value with
+// scale 0.
+func NewDecInt64(x int64) *Dec {
+	return new(Dec).SetUnscaled(big.NewInt(x))
+}
+
+// Scale returns the scale of x.
+func (x *Dec) Scale() Scale {
+	return x.scale
+}
+
+// Unscaled returns the unscaled value of x.
+func (x *Dec) Unscaled() *big.Int {
+	return &x.unscaled
+}
+
+// SetScale sets the scale of x, with the unscaled value unchanged.
+// The mathematical value of the Dec changes as if it was multiplied by
+// 10**(oldscale-scale). 
+func (x *Dec) SetScale(scale Scale) *Dec {
+	x.scale = scale
+	return x
+}
+
+// SetScale sets the unscaled value of x, with the scale unchanged.
+func (x *Dec) SetUnscaled(unscaled *big.Int) *Dec {
+	x.unscaled.Set(unscaled)
+	return x
+}
+
+// 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.SetUnscaled(x.Unscaled())
+		z.SetScale(x.Scale())
+	}
+	return z
+}
+
+// Move sets z to the value of x, and sets x to zero, unless z == x.
+// It is intended for fast assignment from temporary variables without copying
+// the underlying array.
+func (z *Dec) move(x *Dec) *Dec {
+	if z != x {
+		*z = *x
+		*x = Dec{}
+	}
+	return z
+}
+
+// Sign returns:
+//
+//	-1 if x <  0
+//	 0 if x == 0
+//	+1 if x >  0
+//
+func (x *Dec) Sign() int {
+	return x.Unscaled().Sign()
+}
+
+// Neg sets z to -x and returns z.
+func (z *Dec) Neg(x *Dec) *Dec {
+	z.SetScale(x.Scale())
+	z.Unscaled().Neg(x.Unscaled())
+	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.Unscaled().Cmp(yy.Unscaled())
+}
+
+// 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.Unscaled().Abs(x.Unscaled())
+	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.Unscaled().Add(xx.Unscaled(), yy.Unscaled())
+	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.Unscaled().Sub(xx.Unscaled(), yy.Unscaled())
+	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.Unscaled().Mul(x.Unscaled(), y.Unscaled())
+	return z
+}
+
+// Quo sets z to the quotient x/y, with the scale obtained from the given
+// Scaler, rounded using the given Rounder.
+// If the result from the rounder is nil, Quo also 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.
+//
+// See Rounder for details on the various ways for rounding.
+func (z *Dec) Quo(x, y *Dec, scaler Scaler, rounder Rounder) *Dec {
+	s := scaler(x, y)
+	var zzz *Dec
+	if rounder.UseRemainder() {
+		zz, rA, rB := new(Dec).quoRem(x, y, s, true, new(big.Int), new(big.Int))
+		zzz = rounder.Round(new(Dec), zz, rA, rB)
+	} else {
+		zz, _, _ := new(Dec).quoRem(x, y, s, false, nil, nil)
+		zzz = rounder.Round(new(Dec), zz, nil, nil)
+	}
+	if zzz == nil {
+		return nil
+	}
+	return z.move(zzz)
+}
+
+// QuoExact(x, y) is a shorthand for Quo(x, y, ScaleQuoExact, RoundExact).
+// If x/y can be expressed as a Dec without rounding, QuoExact sets z to the
+// quotient x/y and returns z. Otherwise, it returns nil and the value of z is
+// undefined.
+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.Unscaled(), exp10(shift))
+		iy = y.Unscaled()
+	case shift < 0:
+		// decreased scale: decimal-shift divisor left
+		ix = x.Unscaled()
+		iy = new(big.Int).Mul(y.Unscaled(), exp10(-shift))
+	default:
+		ix = x.Unscaled()
+		iy = y.Unscaled()
+	}
+	// save a copy of iy in case it to be overwritten with the result
+	iy2 := iy
+	if iy == z.Unscaled() {
+		iy2 = new(big.Int).Set(iy)
+	}
+	// set scale
+	z.SetScale(s)
+	// set unscaled
+	if useRem {
+		// Int division
+		_, intr := z.Unscaled().QuoRem(ix, iy, new(big.Int))
+		// set remainder
+		remNum.Set(intr)
+		remDen.Set(iy2)
+	} else {
+		z.Unscaled().Quo(ix, iy)
+	}
+	return z, remNum, remDen
+}
+
+// ScaleFixed returns a Scaler with the given fixed result.
+func ScaleFixed(scale Scale) Scaler {
+	return func(x, y *Dec) Scale {
+		return scale
+	}
+}
+
+// ScaleFixed0 is a Scaler that always returns 0. It is intended to be used 
+// with Quo when the result is to be rounded to an integer. 
+var ScaleFixed0 Scaler = ScaleFixed(0)
+
+// ScaleFixed2 is a Scaler that always returns 2. It is intended to be used 
+// with Quo when the result is to be rounded to an decimal with scale 2. 
+var ScaleFixed2 Scaler = ScaleFixed(2)
+
+// ScaleQuoExact is the Scaler used by QuoExact. It returns a scale that is
+// greater than or equal to "x.Scale() - y.Scale()"; it is calculated so that
+// the remainder will be zero whenever x/y is a finite decimal.
+var ScaleQuoExact Scaler = scaleQuoExact
+
+func scaleQuoExact(x, y *Dec) Scale {
+	rem := new(big.Rat).SetFrac(x.Unscaled(), y.Unscaled())
+	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
+}
+
+type rounder struct {
+	useRem bool
+	round  func(z, quo *Dec, remNum, remDen *big.Int) *Dec
+}
+
+func (r rounder) UseRemainder() bool {
+	return r.useRem
+}
+
+func (r rounder) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec {
+	return r.round(z, quo, remNum, remDen)
+}
+
+// RoundExact returns quo if rem is zero, or nil otherwise. It is intended to
+// be used with ScaleQuoExact when it is guaranteed that the result can be
+// obtained without rounding. QuoExact is a shorthand for such a quotient
+// operation. 
+// 
+var RoundExact Rounder = roundExact
+
+// RoundDown rounds towards 0; that is, returns the Dec with the greatest
+// absolute value not exceeding that of the result represented by quo and rem.
+//
+// The following table shows examples of the results for
+// Quo(x, y, ScaleFixed(scale), RoundDown).
+//
+//      x      y    scale   result
+//  ------------------------------
+//    -1.8    10        1     -0.1
+//    -1.5    10        1     -0.1
+//    -1.2    10        1     -0.1
+//    -1.0    10        1     -0.1
+//    -0.8    10        1     -0.0
+//    -0.5    10        1     -0.0
+//    -0.2    10        1     -0.0
+//     0.0    10        1      0.0
+//     0.2    10        1      0.0
+//     0.5    10        1      0.0
+//     0.8    10        1      0.0
+//     1.0    10        1      0.1
+//     1.2    10        1      0.1
+//     1.5    10        1      0.1
+//     1.8    10        1      0.1
+//
+var RoundDown Rounder = roundDown
+
+// RoundUp rounds away from 0; that is, returns the Dec with the smallest
+// absolute value not smaller than that of the result represented by quo and
+// rem.
+//
+// The following table shows examples of the results for
+// Quo(x, y, ScaleFixed(scale), RoundUp).
+//
+//      x      y    scale   result
+//  ------------------------------
+//    -1.8    10        1     -0.2
+//    -1.5    10        1     -0.2
+//    -1.2    10        1     -0.2
+//    -1.0    10        1     -0.1
+//    -0.8    10        1     -0.1
+//    -0.5    10        1     -0.1
+//    -0.2    10        1     -0.1
+//     0.0    10        1      0.0
+//     0.2    10        1      0.1
+//     0.5    10        1      0.1
+//     0.8    10        1      0.1
+//     1.0    10        1      0.1
+//     1.2    10        1      0.2
+//     1.5    10        1      0.2
+//     1.8    10        1      0.2
+//
+var RoundUp Rounder = roundUp
+
+// RoundHalfDown rounds to the nearest Dec, and when the remainder is 1/2, it
+// rounds to the Dec with the lower absolute value.
+//
+// The following table shows examples of the results for
+// Quo(x, y, ScaleFixed(scale), RoundHalfDown).
+//
+//      x      y    scale   result
+//  ------------------------------
+//    -1.8    10        1     -0.2
+//    -1.5    10        1     -0.1
+//    -1.2    10        1     -0.1
+//    -1.0    10        1     -0.1
+//    -0.8    10        1     -0.1
+//    -0.5    10        1     -0.0
+//    -0.2    10        1     -0.0
+//     0.0    10        1      0.0
+//     0.2    10        1      0.0
+//     0.5    10        1      0.0
+//     0.8    10        1      0.1
+//     1.0    10        1      0.1
+//     1.2    10        1      0.1
+//     1.5    10        1      0.1
+//     1.8    10        1      0.2
+//
+var RoundHalfDown Rounder = roundHalfDown
+
+// RoundHalfUp rounds to the nearest Dec, and when the remainder is 1/2, it
+// rounds to the Dec with the greater absolute value.
+//
+// The following table shows examples of the results for
+// Quo(x, y, ScaleFixed(scale), RoundHalfUp).
+//
+//      x      y    scale   result
+//  ------------------------------
+//    -1.8    10        1     -0.2
+//    -1.5    10        1     -0.2
+//    -1.2    10        1     -0.1
+//    -1.0    10        1     -0.1
+//    -0.8    10        1     -0.1
+//    -0.5    10        1     -0.1
+//    -0.2    10        1     -0.0
+//     0.0    10        1      0.0
+//     0.2    10        1      0.0
+//     0.5    10        1      0.1
+//     0.8    10        1      0.1
+//     1.0    10        1      0.1
+//     1.2    10        1      0.1
+//     1.5    10        1      0.2
+//     1.8    10        1      0.2
+//
+var RoundHalfUp Rounder = roundHalfUp
+
+// RoundFloor rounds towards negative infinity; that is, returns the greatest
+// Dec not exceeding the result represented by quo and rem.
+//
+// The following table shows examples of the results for
+// Quo(x, y, ScaleFixed(scale), RoundFloor).
+//
+//      x      y    scale   result
+//  ------------------------------
+//    -1.8    10        1     -0.2
+//    -1.5    10        1     -0.2
+//    -1.2    10        1     -0.2
+//    -1.0    10        1     -0.1
+//    -0.8    10        1     -0.1
+//    -0.5    10        1     -0.1
+//    -0.2    10        1     -0.1
+//     0.0    10        1      0.0
+//     0.2    10        1      0.0
+//     0.5    10        1      0.0
+//     0.8    10        1      0.0
+//     1.0    10        1      0.1
+//     1.2    10        1      0.1
+//     1.5    10        1      0.1
+//     1.8    10        1      0.1
+//
+var RoundFloor Rounder = roundFloor
+
+// RoundCeil rounds towards positive infinity; that is, returns the
+// smallest Dec not smaller than the result represented by quo and rem.
+//
+// The following table shows examples of the results for
+// Quo(x, y, ScaleFixed(scale), RoundCeil).
+//
+//      x      y    scale   result
+//  ------------------------------
+//    -1.8    10        1     -0.1
+//    -1.5    10        1     -0.1
+//    -1.2    10        1     -0.1
+//    -1.0    10        1     -0.1
+//    -0.8    10        1     -0.0
+//    -0.5    10        1     -0.0
+//    -0.2    10        1     -0.0
+//     0.0    10        1      0.0
+//     0.2    10        1      0.1
+//     0.5    10        1      0.1
+//     0.8    10        1      0.1
+//     1.0    10        1      0.1
+//     1.2    10        1      0.2
+//     1.5    10        1      0.2
+//     1.8    10        1      0.2
+//
+var RoundCeil Rounder = roundCeil
+
+var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)}
+
+var roundExact = rounder{true,
+	func(z, q *Dec, rA, rB *big.Int) *Dec {
+		if rA.Sign() != 0 {
+			return nil
+		}
+		return z.move(q)
+	}}
+
+var roundDown = rounder{false,
+	func(z, q *Dec, rA, rB *big.Int) *Dec {
+		return z.move(q)
+	}}
+
+var roundUp = rounder{true,
+	func(z, q *Dec, rA, rB *big.Int) *Dec {
+		z.move(q)
+		if rA.Sign() != 0 {
+			z.Unscaled().Add(z.Unscaled(), intSign[rA.Sign()*rB.Sign()+1])
+		}
+		return z
+	}}
+
+var roundHalfDown = rounder{true,
+	func(z, q *Dec, rA, rB *big.Int) *Dec {
+		z.move(q)
+		brA, brB := rA.BitLen(), rB.BitLen()
+		if brA < brB-1 {
+			// brA < brB-1 => |rA| < |rB/2|
+			return z
+		}
+		adjust := false
+		srA, srB := rA.Sign(), rB.Sign()
+		s := srA * srB
+		if brA == brB-1 {
+			rA2 := new(big.Int).Lsh(rA, 1)
+			if s < 0 {
+				rA2.Neg(rA2)
+			}
+			if rA2.Cmp(rB)*srB > 0 {
+				adjust = true
+			}
+		} else {
+			// brA > brB-1 => |rA| > |rB/2|
+			adjust = true
+		}
+		if adjust {
+			z.Unscaled().Add(z.Unscaled(), intSign[s+1])
+		}
+		return z
+	}}
+
+var roundHalfUp = rounder{true,
+	func(z, q *Dec, rA, rB *big.Int) *Dec {
+		z.move(q)
+		brA, brB := rA.BitLen(), rB.BitLen()
+		if brA < brB-1 {
+			// brA < brB-1 => |rA| < |rB/2|
+			return z
+		}
+		adjust := false
+		srA, srB := rA.Sign(), rB.Sign()
+		s := srA * srB
+		if brA == brB-1 {
+			rA2 := new(big.Int).Lsh(rA, 1)
+			if s < 0 {
+				rA2.Neg(rA2)
+			}
+			if rA2.Cmp(rB)*srB >= 0 {
+				adjust = true
+			}
+		} else {
+			// brA > brB-1 => |rA| > |rB/2|
+			adjust = true
+		}
+		if adjust {
+			z.Unscaled().Add(z.Unscaled(), intSign[s+1])
+		}
+		return z
+	}}
+
+var roundFloor = rounder{true,
+	func(z, q *Dec, rA, rB *big.Int) *Dec {
+		z.move(q)
+		if rA.Sign()*rB.Sign() < 0 {
+			z.Unscaled().Add(z.Unscaled(), intSign[0])
+		}
+		return z
+	}}
+
+var roundCeil = rounder{true,
+	func(z, q *Dec, rA, rB *big.Int) *Dec {
+		z.move(q)
+		if rA.Sign()*rB.Sign() > 0 {
+			z.Unscaled().Add(z.Unscaled(), intSign[2])
+		}
+		return z
+	}}
+
+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 NewDec(new(big.Int).Quo(x.Unscaled(), e), newScale)
+	case shift > 0:
+		e := exp10(shift)
+		return NewDec(new(big.Int).Mul(x.Unscaled(), 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.Unscaled().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.Unscaled().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.Unscaled().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.Unscaled().GobDecode(buf[:l])
+	if err != nil {
+		return err
+	}
+	z.SetScale(scale(buf[l : l+scaleSize]))
+	return nil
+}

+ 454 - 0
dec/dec_test.go

@@ -0,0 +1,454 @@
+package dec
+
+import (
+	"bytes"
+	"encoding/gob"
+	"fmt"
+	"math/big"
+	"strings"
+	"testing"
+)
+
+type decFunZZ func(z, x, y *Dec) *Dec
+type decArgZZ struct {
+	z, x, y *Dec
+}
+
+var decSumZZ = []decArgZZ{
+	{NewDecInt64(0), NewDecInt64(0), NewDecInt64(0)},
+	{NewDecInt64(1), NewDecInt64(1), NewDecInt64(0)},
+	{NewDecInt64(1111111110), NewDecInt64(123456789), NewDecInt64(987654321)},
+	{NewDecInt64(-1), NewDecInt64(-1), NewDecInt64(0)},
+	{NewDecInt64(864197532), NewDecInt64(-123456789), NewDecInt64(987654321)},
+	{NewDecInt64(-1111111110), NewDecInt64(-123456789), NewDecInt64(-987654321)},
+	{NewDec(big.NewInt(12), 2), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(2), 2)},
+}
+
+var decProdZZ = []decArgZZ{
+	{NewDecInt64(0), NewDecInt64(0), NewDecInt64(0)},
+	{NewDecInt64(0), NewDecInt64(1), NewDecInt64(0)},
+	{NewDecInt64(1), NewDecInt64(1), NewDecInt64(1)},
+	{NewDecInt64(-991 * 991), NewDecInt64(991), NewDecInt64(-991)},
+	{NewDec(big.NewInt(2), 3), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(2), 2)},
+	{NewDec(big.NewInt(2), -3), NewDec(big.NewInt(1), -1), NewDec(big.NewInt(2), -2)},
+	{NewDec(big.NewInt(2), 3), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(2), 2)},
+}
+
+func TestDecSignZ(t *testing.T) {
+	var zero Dec
+	for _, a := range decSumZZ {
+		s := a.z.Sign()
+		e := a.z.Cmp(&zero)
+		if s != e {
+			t.Errorf("got %d; want %d for z = %v", s, e, a.z)
+		}
+	}
+}
+
+func TestDecAbsZ(t *testing.T) {
+	var zero Dec
+	for _, a := range decSumZZ {
+		var z Dec
+		z.Abs(a.z)
+		var e Dec
+		e.Set(a.z)
+		if e.Cmp(&zero) < 0 {
+			e.Sub(&zero, &e)
+		}
+		if z.Cmp(&e) != 0 {
+			t.Errorf("got z = %v; want %v", z, e)
+		}
+	}
+}
+
+func testDecFunZZ(t *testing.T, msg string, f decFunZZ, a decArgZZ) {
+	var z Dec
+	f(&z, a.x, a.y)
+	if (&z).Cmp(a.z) != 0 {
+		t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z)
+	}
+}
+
+func TestDecSumZZ(t *testing.T) {
+	AddZZ := func(z, x, y *Dec) *Dec { return z.Add(x, y) }
+	SubZZ := func(z, x, y *Dec) *Dec { return z.Sub(x, y) }
+	for _, a := range decSumZZ {
+		arg := a
+		testDecFunZZ(t, "AddZZ", AddZZ, arg)
+
+		arg = decArgZZ{a.z, a.y, a.x}
+		testDecFunZZ(t, "AddZZ symmetric", AddZZ, arg)
+
+		arg = decArgZZ{a.x, a.z, a.y}
+		testDecFunZZ(t, "SubZZ", SubZZ, arg)
+
+		arg = decArgZZ{a.y, a.z, a.x}
+		testDecFunZZ(t, "SubZZ symmetric", SubZZ, arg)
+	}
+}
+
+func TestDecProdZZ(t *testing.T) {
+	MulZZ := func(z, x, y *Dec) *Dec { return z.Mul(x, y) }
+	for _, a := range decProdZZ {
+		arg := a
+		testDecFunZZ(t, "MulZZ", MulZZ, arg)
+
+		arg = decArgZZ{a.z, a.y, a.x}
+		testDecFunZZ(t, "MulZZ symmetric", MulZZ, arg)
+	}
+}
+
+var decQuoRemZZZ = []struct {
+	z, x, y  *Dec
+	r        *big.Rat
+	srA, srB int
+}{
+	// basic examples
+	{NewDec(big.NewInt(1), 0), NewDecInt64(2), NewDecInt64(2), big.NewRat(0, 1), 0, 1},
+	{NewDec(big.NewInt(15), 1), NewDecInt64(3), NewDecInt64(2), big.NewRat(0, 1), 0, 1},
+	{NewDec(big.NewInt(1), 1), NewDecInt64(1), NewDecInt64(10), big.NewRat(0, 1), 0, 1},
+	{NewDec(big.NewInt(0), 0), NewDecInt64(2), NewDecInt64(3), big.NewRat(2, 3), 1, 1},
+	{NewDec(big.NewInt(0), 0), NewDecInt64(2), NewDecInt64(6), big.NewRat(1, 3), 1, 1},
+	{NewDec(big.NewInt(1), 1), NewDecInt64(2), NewDecInt64(12), big.NewRat(2, 3), 1, 1},
+
+	// examples from the Go Language Specification
+	{NewDec(big.NewInt(1), 0), NewDecInt64(5), NewDecInt64(3), big.NewRat(2, 3), 1, 1},
+	{NewDec(big.NewInt(-1), 0), NewDecInt64(-5), NewDecInt64(3), big.NewRat(-2, 3), -1, 1},
+	{NewDec(big.NewInt(-1), 0), NewDecInt64(5), NewDecInt64(-3), big.NewRat(-2, 3), 1, -1},
+	{NewDec(big.NewInt(1), 0), NewDecInt64(-5), NewDecInt64(-3), big.NewRat(2, 3), -1, -1},
+}
+
+func TestDecQuoRem(t *testing.T) {
+	for i, a := range decQuoRemZZZ {
+		z, rA, rB := new(Dec), new(big.Int), new(big.Int)
+		s := ScaleQuoExact(a.x, a.y)
+		z.quoRem(a.x, a.y, s, true, rA, rB)
+		if a.z.Cmp(z) != 0 || a.r.Cmp(new(big.Rat).SetFrac(rA, rB)) != 0 {
+			t.Errorf("#%d QuoRemZZZ got %v, %v, %v; expected %v, %v", i, z, rA, rB, a.z, a.r)
+		}
+		if a.srA != rA.Sign() || a.srB != rB.Sign() {
+			t.Errorf("#%d QuoRemZZZ wrong signs, got %v, %v; expected %v, %v", i, rA.Sign(), rB.Sign(), a.srA, a.srB)
+		}
+	}
+}
+
+var rounderInputs = [...]struct {
+	quo    *Dec
+	rA, rB *big.Int
+}{
+	// examples from go language spec
+	{NewDec(big.NewInt(1), 0), big.NewInt(2), big.NewInt(3)},   //  5 /  3
+	{NewDec(big.NewInt(-1), 0), big.NewInt(-2), big.NewInt(3)}, // -5 /  3
+	{NewDec(big.NewInt(-1), 0), big.NewInt(2), big.NewInt(-3)}, //  5 / -3
+	{NewDec(big.NewInt(1), 0), big.NewInt(-2), big.NewInt(-3)}, // -5 / -3
+	// examples from godoc
+	{NewDec(big.NewInt(-1), 1), big.NewInt(-8), big.NewInt(10)},
+	{NewDec(big.NewInt(-1), 1), big.NewInt(-5), big.NewInt(10)},
+	{NewDec(big.NewInt(-1), 1), big.NewInt(-2), big.NewInt(10)},
+	{NewDec(big.NewInt(0), 1), big.NewInt(-8), big.NewInt(10)},
+	{NewDec(big.NewInt(0), 1), big.NewInt(-5), big.NewInt(10)},
+	{NewDec(big.NewInt(0), 1), big.NewInt(-2), big.NewInt(10)},
+	{NewDec(big.NewInt(0), 1), big.NewInt(0), big.NewInt(1)},
+	{NewDec(big.NewInt(0), 1), big.NewInt(2), big.NewInt(10)},
+	{NewDec(big.NewInt(0), 1), big.NewInt(5), big.NewInt(10)},
+	{NewDec(big.NewInt(0), 1), big.NewInt(8), big.NewInt(10)},
+	{NewDec(big.NewInt(1), 1), big.NewInt(2), big.NewInt(10)},
+	{NewDec(big.NewInt(1), 1), big.NewInt(5), big.NewInt(10)},
+	{NewDec(big.NewInt(1), 1), big.NewInt(8), big.NewInt(10)},
+}
+
+var rounderResults = [...]struct {
+	rounder Rounder
+	results [len(rounderInputs)]*Dec
+}{
+	{RoundExact, [...]*Dec{nil, nil, nil, nil,
+		nil, nil, nil, nil, nil, nil,
+		NewDec(big.NewInt(0), 1), nil, nil, nil, nil, nil, nil}},
+	{RoundDown, [...]*Dec{
+		NewDecInt64(1), NewDecInt64(-1), NewDecInt64(-1), NewDecInt64(1),
+		NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1),
+		NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1)}},
+	{RoundUp, [...]*Dec{
+		NewDecInt64(2), NewDecInt64(-2), NewDecInt64(-2), NewDecInt64(2),
+		NewDec(big.NewInt(-2), 1), NewDec(big.NewInt(-2), 1), NewDec(big.NewInt(-2), 1),
+		NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1),
+		NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1),
+		NewDec(big.NewInt(2), 1), NewDec(big.NewInt(2), 1), NewDec(big.NewInt(2), 1)}},
+	{RoundHalfDown, [...]*Dec{
+		NewDecInt64(2), NewDecInt64(-2), NewDecInt64(-2), NewDecInt64(2),
+		NewDec(big.NewInt(-2), 1), NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1),
+		NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1), NewDec(big.NewInt(1), 1),
+		NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(2), 1)}},
+	{RoundHalfUp, [...]*Dec{
+		NewDecInt64(2), NewDecInt64(-2), NewDecInt64(-2), NewDecInt64(2),
+		NewDec(big.NewInt(-2), 1), NewDec(big.NewInt(-2), 1), NewDec(big.NewInt(-1), 1),
+		NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(0), 1), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1),
+		NewDec(big.NewInt(1), 1), NewDec(big.NewInt(2), 1), NewDec(big.NewInt(2), 1)}},
+	{RoundFloor, [...]*Dec{
+		NewDecInt64(1), NewDecInt64(-2), NewDecInt64(-2), NewDecInt64(1),
+		NewDec(big.NewInt(-2), 1), NewDec(big.NewInt(-2), 1), NewDec(big.NewInt(-2), 1),
+		NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1),
+		NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1)}},
+	{RoundCeil, [...]*Dec{
+		NewDecInt64(2), NewDecInt64(-1), NewDecInt64(-1), NewDecInt64(2),
+		NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1), NewDec(big.NewInt(-1), 1),
+		NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1), NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(0), 1),
+		NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1), NewDec(big.NewInt(1), 1),
+		NewDec(big.NewInt(2), 1), NewDec(big.NewInt(2), 1), NewDec(big.NewInt(2), 1)}},
+}
+
+func TestRound(t *testing.T) {
+	for i, a := range rounderResults {
+		for j, input := range rounderInputs {
+			q := new(Dec).Set(input.quo)
+			rA, rB := new(big.Int).Set(input.rA), new(big.Int).Set(input.rB)
+			res := a.rounder.Round(new(Dec), q, rA, rB)
+			if a.results[j] == nil && res == nil {
+				continue
+			}
+			if (a.results[j] == nil && res != nil) ||
+				(a.results[j] != nil && res == nil) ||
+				a.results[j].Cmp(res) != 0 {
+				t.Errorf("#%d,%d Rounder got %v; expected %v", i, j, res, a.results[j])
+			}
+		}
+	}
+}
+
+var decStringTests = []struct {
+	in     string
+	out    string
+	val    int64
+	scale  Scale // skip SetString if negative
+	ok     bool
+	scanOk bool
+}{
+	{in: "", ok: false, scanOk: false},
+	{in: "a", ok: false, scanOk: false},
+	{in: "z", ok: false, scanOk: false},
+	{in: "+", ok: false, scanOk: false},
+	{in: "-", ok: false, scanOk: false},
+	{in: "g", ok: false, scanOk: false},
+	{in: ".", ok: false, scanOk: false},
+	{in: ".-0", ok: false, scanOk: false},
+	{in: ".+0", ok: false, scanOk: false},
+	// Scannable but not SetStringable
+	{"0b", "ignored", 0, 0, false, true},
+	{"0x", "ignored", 0, 0, false, true},
+	{"0xg", "ignored", 0, 0, false, true},
+	{"0.0g", "ignored", 0, 1, false, true},
+	// examples from godoc for Dec 
+	{"0", "0", 0, 0, true, true},
+	{"0.00", "0.00", 0, 2, true, true},
+	{"ignored", "0", 0, -2, true, false},
+	{"1", "1", 1, 0, true, true},
+	{"1.00", "1.00", 100, 2, true, true},
+	{"10", "10", 10, 0, true, true},
+	{"ignored", "10", 1, -1, true, false},
+	// other tests
+	{"+0", "0", 0, 0, true, true},
+	{"-0", "0", 0, 0, true, true},
+	{"0.0", "0.0", 0, 1, true, true},
+	{"0.1", "0.1", 1, 1, true, true},
+	{"0.", "0", 0, 0, true, true},
+	{"-10", "-10", -1, -1, true, true},
+	{"-1", "-1", -1, 0, true, true},
+	{"-0.1", "-0.1", -1, 1, true, true},
+	{"-0.01", "-0.01", -1, 2, true, true},
+	{"+0.", "0", 0, 0, true, true},
+	{"-0.", "0", 0, 0, true, true},
+	{".0", "0.0", 0, 1, true, true},
+	{"+.0", "0.0", 0, 1, true, true},
+	{"-.0", "0.0", 0, 1, true, true},
+	{"0.0000000000", "0.0000000000", 0, 10, true, true},
+	{"0.0000000001", "0.0000000001", 1, 10, true, true},
+	{"-0.0000000000", "0.0000000000", 0, 10, true, true},
+	{"-0.0000000001", "-0.0000000001", -1, 10, true, true},
+	{"-10", "-10", -10, 0, true, true},
+	{"+10", "10", 10, 0, true, true},
+	{"00", "0", 0, 0, true, true},
+	{"023", "23", 23, 0, true, true},      // decimal, not octal
+	{"-02.3", "-2.3", -23, 1, true, true}, // decimal, not octal
+}
+
+func TestDecGetString(t *testing.T) {
+	z := new(Dec)
+	for i, test := range decStringTests {
+		if !test.ok {
+			continue
+		}
+		z.SetUnscaled(big.NewInt(test.val))
+		z.SetScale(test.scale)
+
+		s := z.String()
+		if s != test.out {
+			t.Errorf("#%da got %s; want %s", i, s, test.out)
+		}
+
+		s = fmt.Sprintf("%d", z)
+		if s != test.out {
+			t.Errorf("#%db got %s; want %s", i, s, test.out)
+		}
+	}
+}
+
+func TestDecSetString(t *testing.T) {
+	tmp := new(Dec)
+	for i, test := range decStringTests {
+		if test.scale < 0 {
+			// SetString only supports scale >= 0
+			continue
+		}
+		// initialize to a non-zero value so that issues with parsing
+		// 0 are detected
+		tmp.Set(NewDecInt64(1234567890).SetScale(123))
+		n1, ok1 := new(Dec).SetString(test.in)
+		n2, ok2 := tmp.SetString(test.in)
+		expected := NewDecInt64(test.val).SetScale(test.scale)
+		if ok1 != test.ok || ok2 != test.ok {
+			t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok)
+			continue
+		}
+		if !ok1 {
+			if n1 != nil {
+				t.Errorf("#%d (input '%s') n1 != nil", i, test.in)
+			}
+			continue
+		}
+		if !ok2 {
+			if n2 != nil {
+				t.Errorf("#%d (input '%s') n2 != nil", i, test.in)
+			}
+			continue
+		}
+
+		if n1.Cmp(expected) != 0 {
+			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val)
+		}
+		if n2.Cmp(expected) != 0 {
+			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val)
+		}
+	}
+}
+
+func TestDecScan(t *testing.T) {
+	tmp := new(Dec)
+	for i, test := range decStringTests {
+		if test.scale < 0 {
+			// SetString only supports scale >= 0
+			continue
+		}
+		// initialize to a non-zero value so that issues with parsing
+		// 0 are detected
+		tmp.Set(NewDecInt64(1234567890).SetScale(123))
+		n1, n2 := new(Dec), tmp
+		nn1, err1 := fmt.Sscan(test.in, n1)
+		nn2, err2 := fmt.Sscan(test.in, n2)
+		if !test.scanOk {
+			if err1 == nil || err2 == nil {
+				t.Errorf("#%d (input '%s') ok incorrect, should be %t", i, test.in, test.scanOk)
+			}
+			continue
+		}
+		expected := NewDecInt64(test.val).SetScale(test.scale)
+		if nn1 != 1 || err1 != nil || nn2 != 1 || err2 != nil {
+			t.Errorf("#%d (input '%s') error %d %v, %d %v", i, test.in, nn1, err1, nn2, err2)
+			continue
+		}
+		if n1.Cmp(expected) != 0 {
+			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val)
+		}
+		if n2.Cmp(expected) != 0 {
+			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val)
+		}
+	}
+}
+
+var decScanNextTests = []struct {
+	in   string
+	ok   bool
+	next rune
+}{
+	{"", false, 0},
+	{"a", false, 'a'},
+	{"z", false, 'z'},
+	{"+", false, 0},
+	{"-", false, 0},
+	{"g", false, 'g'},
+	{".", false, 0},
+	{".-0", false, '-'},
+	{".+0", false, '+'},
+	{"0b", true, 'b'},
+	{"0x", true, 'x'},
+	{"0xg", true, 'x'},
+	{"0.0g", true, 'g'},
+}
+
+func TestDecScanNext(t *testing.T) {
+	for i, test := range decScanNextTests {
+		rdr := strings.NewReader(test.in)
+		n1 := new(Dec)
+		nn1, _ := fmt.Fscan(rdr, n1)
+		if (test.ok && nn1 == 0) || (!test.ok && nn1 > 0) {
+			t.Errorf("#%d (input '%s') ok incorrect should be %t", i, test.in, test.ok)
+			continue
+		}
+		r := rune(0)
+		nn2, err := fmt.Fscanf(rdr, "%c", &r)
+		if test.next != r {
+			t.Errorf("#%d (input '%s') next incorrect, got %c should be %c, %d, %v", i, test.in, r, test.next, nn2, err)
+		}
+	}
+}
+
+var decGobEncodingTests = []string{
+	"0",
+	"1",
+	"2",
+	"10",
+	"42",
+	"1234567890",
+	"298472983472983471903246121093472394872319615612417471234712061",
+}
+
+func TestDecGobEncoding(t *testing.T) {
+	var medium bytes.Buffer
+	enc := gob.NewEncoder(&medium)
+	dec := gob.NewDecoder(&medium)
+	for i, test := range decGobEncodingTests {
+		for j := 0; j < 2; j++ {
+			for k := Scale(-5); k <= 5; k++ {
+				medium.Reset() // empty buffer for each test case (in case of failures)
+				stest := test
+				if j != 0 {
+					// negative numbers
+					stest = "-" + test
+				}
+				var tx Dec
+				tx.SetString(stest)
+				tx.SetScale(k) // test with positive, negative, and zero scale 
+				if err := enc.Encode(&tx); err != nil {
+					t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err)
+				}
+				var rx Dec
+				if err := dec.Decode(&rx); err != nil {
+					t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err)
+				}
+				if rx.Cmp(&tx) != 0 {
+					t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx)
+				}
+			}
+		}
+	}
+}

+ 62 - 0
dec/example_test.go

@@ -0,0 +1,62 @@
+package dec_test
+
+import (
+	"fmt"
+	"log"
+)
+
+import "code.google.com/p/godec/dec"
+
+func ExampleDec_SetString() {
+	d := new(dec.Dec)
+	d.SetString("012345.67890") // decimal; leading 0 ignored; trailing 0 kept
+	fmt.Println(d)
+	// Output: 12345.67890
+}
+
+func ExampleDec_Scan() {
+	// The Scan function is rarely used directly;
+	// the fmt package recognizes it as an implementation of fmt.Scanner.
+	d := new(dec.Dec)
+	_, err := fmt.Sscan("184467440.73709551617", d)
+	if err != nil {
+		log.Println("error scanning value:", err)
+	} else {
+		fmt.Println(d)
+	}
+	// Output: 184467440.73709551617
+}
+
+func ExampleDec_Quo_scaleFixed2RoundDown() {
+	// 10 / 3 is an infinite decimal; it has no exact Dec representation
+	x, y := dec.NewDecInt64(10), dec.NewDecInt64(3)
+	// use 2 digits beyond the decimal point, round towards 0
+	z := new(dec.Dec).Quo(x, y, dec.ScaleFixed2, dec.RoundDown)
+	fmt.Println(z)
+	// Output: 3.33
+}
+
+func ExampleDec_Quo_scaleFixed0RoundCeil() {
+	// -42 / 400 is an finite decimal with 3 digits beyond the decimal point
+	x, y := dec.NewDecInt64(-42), dec.NewDecInt64(400)
+	// use 2 digits beyond decimal point, round towards positive infinity
+	z := new(dec.Dec).Quo(x, y, dec.ScaleFixed2, dec.RoundCeil)
+	fmt.Println(z)
+	// Output: -0.10
+}
+
+func ExampleDec_QuoExact_ok() {
+	// 1 / 25 is a finite decimal; it has exact Dec representation
+	x, y := dec.NewDecInt64(1), dec.NewDecInt64(25)
+	z := new(dec.Dec).QuoExact(x, y)
+	fmt.Println(z)
+	// Output: 0.04
+}
+
+func ExampleDec_QuoExact_fail() {
+	// 1 / 3 is an infinite decimal; it has no exact Dec representation
+	x, y := dec.NewDecInt64(1), dec.NewDecInt64(3)
+	z := new(dec.Dec).QuoExact(x, y)
+	fmt.Println(z)
+	// Output: <nil>
+}