Browse Source

API cleanup: Quo->QuoRound, unexport Rounder declaration and Scaler type

speter 13 years ago
parent
commit
0f940f59cf
5 changed files with 78 additions and 70 deletions
  1. 4 4
      benchmark_test.go
  2. 48 42
      dec.go
  3. 1 1
      dec_test.go
  4. 4 4
      example_test.go
  5. 21 19
      rounder.go

+ 4 - 4
benchmark_test.go

@@ -140,15 +140,15 @@ func Benchmark_Dec_Mul_QuoExact(b *testing.B) {
 	})
 }
 
-func Benchmark_Dec_Quo_Fixed_Down(b *testing.B) {
+func Benchmark_Dec_QuoRound_Fixed_Down(b *testing.B) {
 	doBenchmarkDec2(b, func(x, y *Dec) {
-		_ = new(Dec).Quo(x, y, Scale(0), RoundDown)
+		_ = new(Dec).QuoRound(x, y, 0, RoundDown)
 	})
 }
 
-func Benchmark_Dec_Quo_Fixed_HalfUp(b *testing.B) {
+func Benchmark_Dec_QuoRound_Fixed_HalfUp(b *testing.B) {
 	doBenchmarkDec2(b, func(x, y *Dec) {
-		_ = new(Dec).Quo(x, y, Scale(0), RoundHalfUp)
+		_ = new(Dec).QuoRound(x, y, 0, RoundHalfUp)
 	})
 }
 

+ 48 - 42
dec.go

@@ -17,20 +17,9 @@
 // Features considered for possible addition:
 //  + formatting options
 //  + Exp method
+//  + combined operations such as AddRound/MulAdd etc
 //  + exchanging data in decimal32/64/128 formats
 //
-// 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 inf
 
 import (
@@ -67,6 +56,23 @@ import (
 //
 // The zero value for a Dec represents the value 0 with scale 0.
 //
+// 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
@@ -79,17 +85,10 @@ 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 {
+type scaler interface {
 	Scale(x *Dec, y *Dec) Scale
 }
 
-// Scale() for a Scale value always returns the Scale value. This allows a Scale
-// value to be used as a Scaler when the desired scale is independent of the
-// values x and y.
-func (s Scale) Scale(x *Dec, y *Dec) Scale {
-	return s
-}
-
 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),
@@ -226,27 +225,31 @@ func (z *Dec) Mul(x, y *Dec) *Dec {
 // 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.Quo(x, NewDecInt64(1), s, r)
+	return z.QuoRound(x, NewDecInt64(1), s, r)
 }
 
-// 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.
+// 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.
 //
-// See Rounder for details on the various ways for rounding.
-func (z *Dec) Quo(x, y *Dec, scaler Scaler, rounder Rounder) *Dec {
-	s := scaler.Scale(x, y)
+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 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)
+	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, s, false, nil, nil)
-		zzz = rounder.Round(new(Dec), zz, nil, nil)
+		zz, _, _ := new(Dec).quoRem(x, y, scl, false, nil, nil)
+		zzz = r.Round(new(Dec), zz, nil, nil)
 	}
 	if zzz == nil {
 		return nil
@@ -254,12 +257,14 @@ func (z *Dec) Quo(x, y *Dec, scaler Scaler, rounder Rounder) *Dec {
 	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.
+// 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)
+	return z.quo(x, y, scaleQuoExact{}, RoundExact)
 }
 
 // quoRem sets z to the quotient x/y with the scale s, and if useRem is true,
@@ -312,10 +317,11 @@ func (z *Dec) quoRem(x, y *Dec, s Scale, useRem bool,
 	return z, remNum, remDen
 }
 
-// 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{}
+type sclr struct{ s Scale }
+
+func (s sclr) Scale(x, y *Dec) Scale {
+	return s.s
+}
 
 type scaleQuoExact struct{}
 

+ 1 - 1
dec_test.go

@@ -121,7 +121,7 @@ var decQuoRemZZZ = []struct {
 func TestDecQuoRem(t *testing.T) {
 	for i, a := range decQuoRemZZZ {
 		z, rA, rB := new(Dec), new(big.Int), new(big.Int)
-		s := ScaleQuoExact.Scale(a.x, a.y)
+		s := scaleQuoExact{}.Scale(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)

+ 4 - 4
example_test.go

@@ -27,20 +27,20 @@ func ExampleDec_Scan() {
 	// Output: 184467440.73709551617
 }
 
-func ExampleDec_Quo_scale2RoundDown() {
+func ExampleDec_QuoRound_scale2RoundDown() {
 	// 10 / 3 is an infinite decimal; it has no exact Dec representation
 	x, y := inf.NewDecInt64(10), inf.NewDecInt64(3)
 	// use 2 digits beyond the decimal point, round towards 0
-	z := new(inf.Dec).Quo(x, y, inf.Scale(2), inf.RoundDown)
+	z := new(inf.Dec).QuoRound(x, y, 2, inf.RoundDown)
 	fmt.Println(z)
 	// Output: 3.33
 }
 
-func ExampleDec_Quo_scale2RoundCeil() {
+func ExampleDec_QuoRound_scale2RoundCeil() {
 	// -42 / 400 is an finite decimal with 3 digits beyond the decimal point
 	x, y := inf.NewDecInt64(-42), inf.NewDecInt64(400)
 	// use 2 digits beyond decimal point, round towards positive infinity
-	z := new(inf.Dec).Quo(x, y, inf.Scale(2), inf.RoundCeil)
+	z := new(inf.Dec).QuoRound(x, y, 2, inf.RoundCeil)
 	fmt.Println(z)
 	// Output: -0.10
 }

+ 21 - 19
rounder.go

@@ -8,7 +8,9 @@ import (
 // result of a division to a finite Dec. It is used by Dec.Round() and
 // Dec.Quo().
 //
-type Rounder interface {
+type Rounder rounder
+
+type rounder interface {
 
 	// When UseRemainder() returns true, the Round() method is passed the
 	// remainder of the division, expressed as the numerator and denominator of
@@ -29,16 +31,16 @@ type Rounder interface {
 	Round(z, quo *Dec, remNum, remDen *big.Int) *Dec
 }
 
-type rounder struct {
+type rndr struct {
 	useRem bool
 	round  func(z, quo *Dec, remNum, remDen *big.Int) *Dec
 }
 
-func (r rounder) UseRemainder() bool {
+func (r rndr) UseRemainder() bool {
 	return r.useRem
 }
 
-func (r rounder) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec {
+func (r rndr) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec {
 	return r.round(z, quo, remNum, remDen)
 }
 
@@ -53,7 +55,7 @@ var RoundExact Rounder = roundExact
 // 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, Scale(scale), RoundDown).
+// Quo(x, y, scale, RoundDown).
 //
 //      x      y    scale   result
 //  ------------------------------
@@ -80,7 +82,7 @@ var RoundDown Rounder = roundDown
 // rem.
 //
 // The following table shows examples of the results for
-// Quo(x, y, Scale(scale), RoundUp).
+// Quo(x, y, scale, RoundUp).
 //
 //      x      y    scale   result
 //  ------------------------------
@@ -106,7 +108,7 @@ var RoundUp Rounder = roundUp
 // rounds to the Dec with the lower absolute value.
 //
 // The following table shows examples of the results for
-// Quo(x, y, Scale(scale), RoundHalfDown).
+// Quo(x, y, scale, RoundHalfDown).
 //
 //      x      y    scale   result
 //  ------------------------------
@@ -132,7 +134,7 @@ var RoundHalfDown Rounder = roundHalfDown
 // rounds to the Dec with the greater absolute value.
 //
 // The following table shows examples of the results for
-// Quo(x, y, Scale(scale), RoundHalfUp).
+// Quo(x, y, scale, RoundHalfUp).
 //
 //      x      y    scale   result
 //  ------------------------------
@@ -158,7 +160,7 @@ var RoundHalfUp Rounder = roundHalfUp
 // rounds to the Dec with even last digit.
 //
 // The following table shows examples of the results for
-// Quo(x, y, Scale(scale), RoundHalfEven).
+// Quo(x, y, scale, RoundHalfEven).
 //
 //      x      y    scale   result
 //  ------------------------------
@@ -184,7 +186,7 @@ var RoundHalfEven Rounder = roundHalfEven
 // Dec not exceeding the result represented by quo and rem.
 //
 // The following table shows examples of the results for
-// Quo(x, y, Scale(scale), RoundFloor).
+// Quo(x, y, scale, RoundFloor).
 //
 //      x      y    scale   result
 //  ------------------------------
@@ -210,7 +212,7 @@ var RoundFloor Rounder = roundFloor
 // smallest Dec not smaller than the result represented by quo and rem.
 //
 // The following table shows examples of the results for
-// Quo(x, y, Scale(scale), RoundCeil).
+// Quo(x, y, scale, RoundCeil).
 //
 //      x      y    scale   result
 //  ------------------------------
@@ -234,7 +236,7 @@ var RoundCeil Rounder = roundCeil
 
 var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)}
 
-var roundExact = rounder{true,
+var roundExact = rndr{true,
 	func(z, q *Dec, rA, rB *big.Int) *Dec {
 		if rA.Sign() != 0 {
 			return nil
@@ -242,12 +244,12 @@ var roundExact = rounder{true,
 		return z.move(q)
 	}}
 
-var roundDown = rounder{false,
+var roundDown = rndr{false,
 	func(z, q *Dec, rA, rB *big.Int) *Dec {
 		return z.move(q)
 	}}
 
-var roundUp = rounder{true,
+var roundUp = rndr{true,
 	func(z, q *Dec, rA, rB *big.Int) *Dec {
 		z.move(q)
 		if rA.Sign() != 0 {
@@ -284,22 +286,22 @@ func roundHalf(f func(c int, odd uint) (roundUp bool)) func(z, q *Dec, rA, rB *b
 	}
 }
 
-var roundHalfDown = rounder{true, roundHalf(
+var roundHalfDown = rndr{true, roundHalf(
 	func(c int, odd uint) bool {
 		return c > 0
 	})}
 
-var roundHalfUp = rounder{true, roundHalf(
+var roundHalfUp = rndr{true, roundHalf(
 	func(c int, odd uint) bool {
 		return c >= 0
 	})}
 
-var roundHalfEven = rounder{true, roundHalf(
+var roundHalfEven = rndr{true, roundHalf(
 	func(c int, odd uint) bool {
 		return c > 0 || c == 0 && odd == 1
 	})}
 
-var roundFloor = rounder{true,
+var roundFloor = rndr{true,
 	func(z, q *Dec, rA, rB *big.Int) *Dec {
 		z.move(q)
 		if rA.Sign()*rB.Sign() < 0 {
@@ -308,7 +310,7 @@ var roundFloor = rounder{true,
 		return z
 	}}
 
-var roundCeil = rounder{true,
+var roundCeil = rndr{true,
 	func(z, q *Dec, rA, rB *big.Int) *Dec {
 		z.move(q)
 		if rA.Sign()*rB.Sign() > 0 {