]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: introduce Bits abstraction instead of using "untyped" []int bit lists
authorRobert Griesemer <gri@golang.org>
Wed, 4 Mar 2015 22:53:41 +0000 (14:53 -0800)
committerRobert Griesemer <gri@golang.org>
Thu, 12 Mar 2015 18:31:21 +0000 (18:31 +0000)
Change-Id: I6caa6bdcf6643ce3015244397a752bd133f3d00c
Reviewed-on: https://go-review.googlesource.com/6840
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/bits_test.go
src/math/big/float.go
src/math/big/float_test.go

index ef6ade011548c17c4f0f9dac3f5c5b87506f5e55..761f75628f8750bf8d138ce9a511f224d2a9866b 100644 (file)
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// This file implements the Bits type used for testing Float operations
+// via an independent (albeit slower) representations for floating-point
+// numbers.
+
 package big
 
 import (
@@ -10,12 +14,23 @@ import (
        "testing"
 )
 
-func addBits(x, y []int) []int {
+// A Bits value b represents a finite floating-point number x of the form
+//
+//     x = 2**b[0] + 2**b[1] + ... 2**b[len(b)-1]
+//
+// The order of slice elements is not significant. Negative elements may be
+// used to form fractions. A Bits value is normalized if each b[i] occurs at
+// most once. For instance Bits{0, 0, 1} is not normalized but represents the
+// same floating-point number as Bits{2}, which is normalized. The zero (nil)
+// value of Bits is a ready to use Bits value and represents the value 0.
+type Bits []int
+
+func (x Bits) add(y Bits) Bits {
        return append(x, y...)
 }
 
-func mulBits(x, y []int) []int {
-       var p []int
+func (x Bits) mul(y Bits) Bits {
+       var p Bits
        for _, x := range x {
                for _, y := range y {
                        p = append(p, x+y)
@@ -26,17 +41,17 @@ func mulBits(x, y []int) []int {
 
 func TestMulBits(t *testing.T) {
        for _, test := range []struct {
-               x, y, want []int
+               x, y, want Bits
        }{
                {nil, nil, nil},
-               {[]int{}, []int{}, nil},
-               {[]int{0}, []int{0}, []int{0}},
-               {[]int{0}, []int{1}, []int{1}},
-               {[]int{1}, []int{1, 2, 3}, []int{2, 3, 4}},
-               {[]int{-1}, []int{1}, []int{0}},
-               {[]int{-10, -1, 0, 1, 10}, []int{1, 2, 3}, []int{-9, -8, -7, 0, 1, 2, 1, 2, 3, 2, 3, 4, 11, 12, 13}},
+               {Bits{}, Bits{}, nil},
+               {Bits{0}, Bits{0}, Bits{0}},
+               {Bits{0}, Bits{1}, Bits{1}},
+               {Bits{1}, Bits{1, 2, 3}, Bits{2, 3, 4}},
+               {Bits{-1}, Bits{1}, Bits{0}},
+               {Bits{-10, -1, 0, 1, 10}, Bits{1, 2, 3}, Bits{-9, -8, -7, 0, 1, 2, 1, 2, 3, 2, 3, 4, 11, 12, 13}},
        } {
-               got := fmt.Sprintf("%v", mulBits(test.x, test.y))
+               got := fmt.Sprintf("%v", test.x.mul(test.y))
                want := fmt.Sprintf("%v", test.want)
                if got != want {
                        t.Errorf("%v * %v = %s; want %s", test.x, test.y, got, want)
@@ -45,12 +60,10 @@ func TestMulBits(t *testing.T) {
        }
 }
 
-// normBits returns the normalized bits for x: It
-// removes multiple equal entries by treating them
-// as an addition (e.g., []int{5, 5} => []int{6}),
-// and it sorts the result list for reproducible
-// results.
-func normBits(x []int) []int {
+// norm returns the normalized bits for x: It removes multiple equal entries
+// by treating them as an addition (e.g., Bits{5, 5} => Bits{6}), and it sorts
+// the result list for reproducible results.
+func (x Bits) norm() Bits {
        m := make(map[int]bool)
        for _, b := range x {
                for m[b] {
@@ -59,28 +72,28 @@ func normBits(x []int) []int {
                }
                m[b] = true
        }
-       var z []int
+       var z Bits
        for b, set := range m {
                if set {
                        z = append(z, b)
                }
        }
-       sort.Ints(z)
+       sort.Ints([]int(z))
        return z
 }
 
 func TestNormBits(t *testing.T) {
        for _, test := range []struct {
-               x, want []int
+               x, want Bits
        }{
                {nil, nil},
-               {[]int{}, []int{}},
-               {[]int{0}, []int{0}},
-               {[]int{0, 0}, []int{1}},
-               {[]int{3, 1, 1}, []int{2, 3}},
-               {[]int{10, 9, 8, 7, 6, 6}, []int{11}},
+               {Bits{}, Bits{}},
+               {Bits{0}, Bits{0}},
+               {Bits{0, 0}, Bits{1}},
+               {Bits{3, 1, 1}, Bits{2, 3}},
+               {Bits{10, 9, 8, 7, 6, 6}, Bits{11}},
        } {
-               got := fmt.Sprintf("%v", normBits(test.x))
+               got := fmt.Sprintf("%v", test.x.norm())
                want := fmt.Sprintf("%v", test.want)
                if got != want {
                        t.Errorf("normBits(%v) = %s; want %s", test.x, got, want)
@@ -89,10 +102,10 @@ func TestNormBits(t *testing.T) {
        }
 }
 
-// roundBits returns the Float value rounded to prec bits
-// according to mode from the bit set x.
-func roundBits(x []int, prec uint, mode RoundingMode) *Float {
-       x = normBits(x)
+// round returns the Float value corresponding to x after rounding x
+// to prec bits according to mode.
+func (x Bits) round(prec uint, mode RoundingMode) *Float {
+       x = x.norm()
 
        // determine range
        var min, max int
@@ -106,13 +119,13 @@ func roundBits(x []int, prec uint, mode RoundingMode) *Float {
        }
        prec0 := uint(max + 1 - min)
        if prec >= prec0 {
-               return fromBits(x)
+               return x.Float()
        }
        // prec < prec0
 
        // determine bit 0, rounding, and sticky bit, and result bits z
        var bit0, rbit, sbit uint
-       var z []int
+       var z Bits
        r := max - int(prec)
        for _, b := range x {
                switch {
@@ -130,23 +143,22 @@ func roundBits(x []int, prec uint, mode RoundingMode) *Float {
        }
 
        // round
-       f := fromBits(z) // rounded to zero
+       f := z.Float() // rounded to zero
        if mode == ToNearestAway {
                panic("not yet implemented")
        }
        if mode == ToNearestEven && rbit == 1 && (sbit == 1 || sbit == 0 && bit0 != 0) || mode == AwayFromZero {
                // round away from zero
                f.SetMode(ToZero).SetPrec(prec)
-               f.Add(f, fromBits([]int{int(r) + 1}))
+               f.Add(f, Bits{int(r) + 1}.Float())
        }
        return f
 }
 
-// fromBits returns the *Float z of the smallest possible precision
-// such that z = sum(2**bits[i]), with i = range bits.
-// If multiple bits[i] are equal, they are added: fromBits(0, 1, 0)
-// == 2**1 + 2**0 + 2**0 = 4.
-func fromBits(bits []int) *Float {
+// Float returns the *Float z of the smallest possible precision such that
+// z = sum(2**bits[i]), with i = range bits. If multiple bits[i] are equal,
+// they are added: Bits{0, 1, 0}.Float() == 2**0 + 2**1 + 2**0 = 4.
+func (bits Bits) Float() *Float {
        // handle 0
        if len(bits) == 0 {
                return new(Float)
@@ -181,25 +193,25 @@ func fromBits(bits []int) *Float {
 
 func TestFromBits(t *testing.T) {
        for _, test := range []struct {
-               bits []int
+               bits Bits
                want string
        }{
                // all different bit numbers
                {nil, "0"},
-               {[]int{0}, "0x.8p1"},
-               {[]int{1}, "0x.8p2"},
-               {[]int{-1}, "0x.8p0"},
-               {[]int{63}, "0x.8p64"},
-               {[]int{33, -30}, "0x.8000000000000001p34"},
-               {[]int{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p256"},
+               {Bits{0}, "0x.8p1"},
+               {Bits{1}, "0x.8p2"},
+               {Bits{-1}, "0x.8p0"},
+               {Bits{63}, "0x.8p64"},
+               {Bits{33, -30}, "0x.8000000000000001p34"},
+               {Bits{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p256"},
 
                // multiple equal bit numbers
-               {[]int{0, 0}, "0x.8p2"},
-               {[]int{0, 0, 0, 0}, "0x.8p3"},
-               {[]int{0, 1, 0}, "0x.8p3"},
-               {append([]int{2, 1, 0} /* 7 */, []int{3, 1} /* 10 */ ...), "0x.88p5" /* 17 */},
+               {Bits{0, 0}, "0x.8p2"},
+               {Bits{0, 0, 0, 0}, "0x.8p3"},
+               {Bits{0, 1, 0}, "0x.8p3"},
+               {append(Bits{2, 1, 0} /* 7 */, Bits{3, 1} /* 10 */ ...), "0x.88p5" /* 17 */},
        } {
-               f := fromBits(test.bits)
+               f := test.bits.Float()
                if got := f.Format('p', 0); got != test.want {
                        t.Errorf("setBits(%v) = %s; want %s", test.bits, got, test.want)
                }
index 62d539b7558de0e7d3b7157fccf9cefbcaaa7674..29de05785fa669504db58932a26f942e120b8ebc 100644 (file)
@@ -433,7 +433,7 @@ func (z *Float) round(sbit uint) {
        lsb := Word(1) << t
 
        // make rounding decision
-       // TODO(gri) This can be simplified (see roundBits in float_test.go).
+       // TODO(gri) This can be simplified (see Bits.round in bits_test.go).
        switch mode {
        case ToZero:
                // nothing to do
index 97a54f62b33c1767aa32730fbf932eacbf9735a6..281e099bd678474710dc480c3e166cfa138779e3 100644 (file)
@@ -1006,7 +1006,7 @@ var precList = [...]uint{1, 2, 5, 8, 10, 16, 23, 24, 32, 50, 53, 64, 100, 128, 5
 
 // Selected bits with which to run various tests.
 // Each entry is a list of bits representing a floating-point number (see fromBits).
-var bitsList = [...][]int{
+var bitsList = [...]Bits{
        {},           // = 0
        {0},          // = 1
        {1},          // = 2
@@ -1026,23 +1026,23 @@ func TestFloatAdd(t *testing.T) {
        for _, xbits := range bitsList {
                for _, ybits := range bitsList {
                        // exact values
-                       x := fromBits(xbits)
-                       y := fromBits(ybits)
-                       zbits := addBits(xbits, ybits)
-                       z := fromBits(zbits)
+                       x := xbits.Float()
+                       y := ybits.Float()
+                       zbits := xbits.add(ybits)
+                       z := zbits.Float()
 
                        for i, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
                                for _, prec := range precList {
                                        got := new(Float).SetPrec(prec).SetMode(mode)
                                        got.Add(x, y)
-                                       want := roundBits(zbits, prec, mode)
+                                       want := zbits.round(prec, mode)
                                        if got.Cmp(want) != 0 {
                                                t.Errorf("i = %d, prec = %d, %s:\n\t     %s %v\n\t+    %s %v\n\t=    %s\n\twant %s",
                                                        i, prec, mode, x, xbits, y, ybits, got, want)
                                        }
 
                                        got.Sub(z, x)
-                                       want = roundBits(ybits, prec, mode)
+                                       want = ybits.round(prec, mode)
                                        if got.Cmp(want) != 0 {
                                                t.Errorf("i = %d, prec = %d, %s:\n\t     %s %v\n\t-    %s %v\n\t=    %s\n\twant %s",
                                                        i, prec, mode, z, zbits, x, xbits, got, want)
@@ -1127,16 +1127,16 @@ func TestFloatMul(t *testing.T) {
        for _, xbits := range bitsList {
                for _, ybits := range bitsList {
                        // exact values
-                       x := fromBits(xbits)
-                       y := fromBits(ybits)
-                       zbits := mulBits(xbits, ybits) // x * y
-                       z := fromBits(zbits)
+                       x := xbits.Float()
+                       y := ybits.Float()
+                       zbits := xbits.mul(ybits)
+                       z := zbits.Float()
 
                        for i, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
                                for _, prec := range precList {
                                        got := new(Float).SetPrec(prec).SetMode(mode)
                                        got.Mul(x, y)
-                                       want := roundBits(zbits, prec, mode)
+                                       want := zbits.round(prec, mode)
                                        if got.Cmp(want) != 0 {
                                                t.Errorf("i = %d, prec = %d, %s:\n\t     %s %v\n\t*    %s %v\n\t=    %s\n\twant %s",
                                                        i, prec, mode, x, xbits, y, ybits, got, want)
@@ -1146,7 +1146,7 @@ func TestFloatMul(t *testing.T) {
                                                continue // ignore div-0 case (not invertable)
                                        }
                                        got.Quo(z, x)
-                                       want = roundBits(ybits, prec, mode)
+                                       want = ybits.round(prec, mode)
                                        if got.Cmp(want) != 0 {
                                                t.Errorf("i = %d, prec = %d, %s:\n\t     %s %v\n\t/    %s %v\n\t=    %s\n\twant %s",
                                                        i, prec, mode, z, zbits, x, xbits, got, want)
@@ -1249,7 +1249,7 @@ func TestFloatQuo(t *testing.T) {
 
        for i := 0; i < 8; i++ {
                // compute accurate (not rounded) result z
-               bits := []int{preci - 1}
+               bits := Bits{preci - 1}
                if i&3 != 0 {
                        bits = append(bits, 0)
                }
@@ -1259,7 +1259,7 @@ func TestFloatQuo(t *testing.T) {
                if i&1 != 0 {
                        bits = append(bits, -precf)
                }
-               z := fromBits(bits)
+               z := bits.Float()
 
                // compute accurate x as z*y
                y := new(Float).SetFloat64(3.14159265358979323e123)
@@ -1280,7 +1280,7 @@ func TestFloatQuo(t *testing.T) {
                        for d := -5; d < 5; d++ {
                                prec := uint(preci + d)
                                got := new(Float).SetPrec(prec).SetMode(mode).Quo(x, y)
-                               want := roundBits(bits, prec, mode)
+                               want := bits.round(prec, mode)
                                if got.Cmp(want) != 0 {
                                        t.Errorf("i = %d, prec = %d, %s:\n\t     %s\n\t/    %s\n\t=    %s\n\twant %s",
                                                i, prec, mode, x, y, got, want)