mirror of
https://github.com/golang/go
synced 2024-11-19 06:54:39 -07:00
math/big: introduce Bits abstraction instead of using "untyped" []int bit lists
Change-Id: I6caa6bdcf6643ce3015244397a752bd133f3d00c Reviewed-on: https://go-review.googlesource.com/6840 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
cf6b60c2f1
commit
63269404a8
@ -2,6 +2,10 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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
|
package big
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -10,12 +14,23 @@ import (
|
|||||||
"testing"
|
"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...)
|
return append(x, y...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mulBits(x, y []int) []int {
|
func (x Bits) mul(y Bits) Bits {
|
||||||
var p []int
|
var p Bits
|
||||||
for _, x := range x {
|
for _, x := range x {
|
||||||
for _, y := range y {
|
for _, y := range y {
|
||||||
p = append(p, x+y)
|
p = append(p, x+y)
|
||||||
@ -26,17 +41,17 @@ func mulBits(x, y []int) []int {
|
|||||||
|
|
||||||
func TestMulBits(t *testing.T) {
|
func TestMulBits(t *testing.T) {
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
x, y, want []int
|
x, y, want Bits
|
||||||
}{
|
}{
|
||||||
{nil, nil, nil},
|
{nil, nil, nil},
|
||||||
{[]int{}, []int{}, nil},
|
{Bits{}, Bits{}, nil},
|
||||||
{[]int{0}, []int{0}, []int{0}},
|
{Bits{0}, Bits{0}, Bits{0}},
|
||||||
{[]int{0}, []int{1}, []int{1}},
|
{Bits{0}, Bits{1}, Bits{1}},
|
||||||
{[]int{1}, []int{1, 2, 3}, []int{2, 3, 4}},
|
{Bits{1}, Bits{1, 2, 3}, Bits{2, 3, 4}},
|
||||||
{[]int{-1}, []int{1}, []int{0}},
|
{Bits{-1}, Bits{1}, Bits{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{-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)
|
want := fmt.Sprintf("%v", test.want)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("%v * %v = %s; want %s", test.x, test.y, 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
|
// norm returns the normalized bits for x: It removes multiple equal entries
|
||||||
// removes multiple equal entries by treating them
|
// by treating them as an addition (e.g., Bits{5, 5} => Bits{6}), and it sorts
|
||||||
// as an addition (e.g., []int{5, 5} => []int{6}),
|
// the result list for reproducible results.
|
||||||
// and it sorts the result list for reproducible
|
func (x Bits) norm() Bits {
|
||||||
// results.
|
|
||||||
func normBits(x []int) []int {
|
|
||||||
m := make(map[int]bool)
|
m := make(map[int]bool)
|
||||||
for _, b := range x {
|
for _, b := range x {
|
||||||
for m[b] {
|
for m[b] {
|
||||||
@ -59,28 +72,28 @@ func normBits(x []int) []int {
|
|||||||
}
|
}
|
||||||
m[b] = true
|
m[b] = true
|
||||||
}
|
}
|
||||||
var z []int
|
var z Bits
|
||||||
for b, set := range m {
|
for b, set := range m {
|
||||||
if set {
|
if set {
|
||||||
z = append(z, b)
|
z = append(z, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Ints(z)
|
sort.Ints([]int(z))
|
||||||
return z
|
return z
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNormBits(t *testing.T) {
|
func TestNormBits(t *testing.T) {
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
x, want []int
|
x, want Bits
|
||||||
}{
|
}{
|
||||||
{nil, nil},
|
{nil, nil},
|
||||||
{[]int{}, []int{}},
|
{Bits{}, Bits{}},
|
||||||
{[]int{0}, []int{0}},
|
{Bits{0}, Bits{0}},
|
||||||
{[]int{0, 0}, []int{1}},
|
{Bits{0, 0}, Bits{1}},
|
||||||
{[]int{3, 1, 1}, []int{2, 3}},
|
{Bits{3, 1, 1}, Bits{2, 3}},
|
||||||
{[]int{10, 9, 8, 7, 6, 6}, []int{11}},
|
{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)
|
want := fmt.Sprintf("%v", test.want)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("normBits(%v) = %s; want %s", test.x, 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
|
// round returns the Float value corresponding to x after rounding x
|
||||||
// according to mode from the bit set x.
|
// to prec bits according to mode.
|
||||||
func roundBits(x []int, prec uint, mode RoundingMode) *Float {
|
func (x Bits) round(prec uint, mode RoundingMode) *Float {
|
||||||
x = normBits(x)
|
x = x.norm()
|
||||||
|
|
||||||
// determine range
|
// determine range
|
||||||
var min, max int
|
var min, max int
|
||||||
@ -106,13 +119,13 @@ func roundBits(x []int, prec uint, mode RoundingMode) *Float {
|
|||||||
}
|
}
|
||||||
prec0 := uint(max + 1 - min)
|
prec0 := uint(max + 1 - min)
|
||||||
if prec >= prec0 {
|
if prec >= prec0 {
|
||||||
return fromBits(x)
|
return x.Float()
|
||||||
}
|
}
|
||||||
// prec < prec0
|
// prec < prec0
|
||||||
|
|
||||||
// determine bit 0, rounding, and sticky bit, and result bits z
|
// determine bit 0, rounding, and sticky bit, and result bits z
|
||||||
var bit0, rbit, sbit uint
|
var bit0, rbit, sbit uint
|
||||||
var z []int
|
var z Bits
|
||||||
r := max - int(prec)
|
r := max - int(prec)
|
||||||
for _, b := range x {
|
for _, b := range x {
|
||||||
switch {
|
switch {
|
||||||
@ -130,23 +143,22 @@ func roundBits(x []int, prec uint, mode RoundingMode) *Float {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// round
|
// round
|
||||||
f := fromBits(z) // rounded to zero
|
f := z.Float() // rounded to zero
|
||||||
if mode == ToNearestAway {
|
if mode == ToNearestAway {
|
||||||
panic("not yet implemented")
|
panic("not yet implemented")
|
||||||
}
|
}
|
||||||
if mode == ToNearestEven && rbit == 1 && (sbit == 1 || sbit == 0 && bit0 != 0) || mode == AwayFromZero {
|
if mode == ToNearestEven && rbit == 1 && (sbit == 1 || sbit == 0 && bit0 != 0) || mode == AwayFromZero {
|
||||||
// round away from zero
|
// round away from zero
|
||||||
f.SetMode(ToZero).SetPrec(prec)
|
f.SetMode(ToZero).SetPrec(prec)
|
||||||
f.Add(f, fromBits([]int{int(r) + 1}))
|
f.Add(f, Bits{int(r) + 1}.Float())
|
||||||
}
|
}
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
// fromBits returns the *Float z of the smallest possible precision
|
// Float returns the *Float z of the smallest possible precision such that
|
||||||
// such that z = sum(2**bits[i]), with i = range bits.
|
// z = sum(2**bits[i]), with i = range bits. If multiple bits[i] are equal,
|
||||||
// If multiple bits[i] are equal, they are added: fromBits(0, 1, 0)
|
// they are added: Bits{0, 1, 0}.Float() == 2**0 + 2**1 + 2**0 = 4.
|
||||||
// == 2**1 + 2**0 + 2**0 = 4.
|
func (bits Bits) Float() *Float {
|
||||||
func fromBits(bits []int) *Float {
|
|
||||||
// handle 0
|
// handle 0
|
||||||
if len(bits) == 0 {
|
if len(bits) == 0 {
|
||||||
return new(Float)
|
return new(Float)
|
||||||
@ -181,25 +193,25 @@ func fromBits(bits []int) *Float {
|
|||||||
|
|
||||||
func TestFromBits(t *testing.T) {
|
func TestFromBits(t *testing.T) {
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
bits []int
|
bits Bits
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
// all different bit numbers
|
// all different bit numbers
|
||||||
{nil, "0"},
|
{nil, "0"},
|
||||||
{[]int{0}, "0x.8p1"},
|
{Bits{0}, "0x.8p1"},
|
||||||
{[]int{1}, "0x.8p2"},
|
{Bits{1}, "0x.8p2"},
|
||||||
{[]int{-1}, "0x.8p0"},
|
{Bits{-1}, "0x.8p0"},
|
||||||
{[]int{63}, "0x.8p64"},
|
{Bits{63}, "0x.8p64"},
|
||||||
{[]int{33, -30}, "0x.8000000000000001p34"},
|
{Bits{33, -30}, "0x.8000000000000001p34"},
|
||||||
{[]int{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p256"},
|
{Bits{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p256"},
|
||||||
|
|
||||||
// multiple equal bit numbers
|
// multiple equal bit numbers
|
||||||
{[]int{0, 0}, "0x.8p2"},
|
{Bits{0, 0}, "0x.8p2"},
|
||||||
{[]int{0, 0, 0, 0}, "0x.8p3"},
|
{Bits{0, 0, 0, 0}, "0x.8p3"},
|
||||||
{[]int{0, 1, 0}, "0x.8p3"},
|
{Bits{0, 1, 0}, "0x.8p3"},
|
||||||
{append([]int{2, 1, 0} /* 7 */, []int{3, 1} /* 10 */ ...), "0x.88p5" /* 17 */},
|
{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 {
|
if got := f.Format('p', 0); got != test.want {
|
||||||
t.Errorf("setBits(%v) = %s; want %s", test.bits, got, test.want)
|
t.Errorf("setBits(%v) = %s; want %s", test.bits, got, test.want)
|
||||||
}
|
}
|
||||||
|
@ -433,7 +433,7 @@ func (z *Float) round(sbit uint) {
|
|||||||
lsb := Word(1) << t
|
lsb := Word(1) << t
|
||||||
|
|
||||||
// make rounding decision
|
// 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 {
|
switch mode {
|
||||||
case ToZero:
|
case ToZero:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
@ -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.
|
// Selected bits with which to run various tests.
|
||||||
// Each entry is a list of bits representing a floating-point number (see fromBits).
|
// Each entry is a list of bits representing a floating-point number (see fromBits).
|
||||||
var bitsList = [...][]int{
|
var bitsList = [...]Bits{
|
||||||
{}, // = 0
|
{}, // = 0
|
||||||
{0}, // = 1
|
{0}, // = 1
|
||||||
{1}, // = 2
|
{1}, // = 2
|
||||||
@ -1026,23 +1026,23 @@ func TestFloatAdd(t *testing.T) {
|
|||||||
for _, xbits := range bitsList {
|
for _, xbits := range bitsList {
|
||||||
for _, ybits := range bitsList {
|
for _, ybits := range bitsList {
|
||||||
// exact values
|
// exact values
|
||||||
x := fromBits(xbits)
|
x := xbits.Float()
|
||||||
y := fromBits(ybits)
|
y := ybits.Float()
|
||||||
zbits := addBits(xbits, ybits)
|
zbits := xbits.add(ybits)
|
||||||
z := fromBits(zbits)
|
z := zbits.Float()
|
||||||
|
|
||||||
for i, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
|
for i, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
|
||||||
for _, prec := range precList {
|
for _, prec := range precList {
|
||||||
got := new(Float).SetPrec(prec).SetMode(mode)
|
got := new(Float).SetPrec(prec).SetMode(mode)
|
||||||
got.Add(x, y)
|
got.Add(x, y)
|
||||||
want := roundBits(zbits, prec, mode)
|
want := zbits.round(prec, mode)
|
||||||
if got.Cmp(want) != 0 {
|
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",
|
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)
|
i, prec, mode, x, xbits, y, ybits, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
got.Sub(z, x)
|
got.Sub(z, x)
|
||||||
want = roundBits(ybits, prec, mode)
|
want = ybits.round(prec, mode)
|
||||||
if got.Cmp(want) != 0 {
|
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",
|
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)
|
i, prec, mode, z, zbits, x, xbits, got, want)
|
||||||
@ -1127,16 +1127,16 @@ func TestFloatMul(t *testing.T) {
|
|||||||
for _, xbits := range bitsList {
|
for _, xbits := range bitsList {
|
||||||
for _, ybits := range bitsList {
|
for _, ybits := range bitsList {
|
||||||
// exact values
|
// exact values
|
||||||
x := fromBits(xbits)
|
x := xbits.Float()
|
||||||
y := fromBits(ybits)
|
y := ybits.Float()
|
||||||
zbits := mulBits(xbits, ybits) // x * y
|
zbits := xbits.mul(ybits)
|
||||||
z := fromBits(zbits)
|
z := zbits.Float()
|
||||||
|
|
||||||
for i, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
|
for i, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
|
||||||
for _, prec := range precList {
|
for _, prec := range precList {
|
||||||
got := new(Float).SetPrec(prec).SetMode(mode)
|
got := new(Float).SetPrec(prec).SetMode(mode)
|
||||||
got.Mul(x, y)
|
got.Mul(x, y)
|
||||||
want := roundBits(zbits, prec, mode)
|
want := zbits.round(prec, mode)
|
||||||
if got.Cmp(want) != 0 {
|
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",
|
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)
|
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)
|
continue // ignore div-0 case (not invertable)
|
||||||
}
|
}
|
||||||
got.Quo(z, x)
|
got.Quo(z, x)
|
||||||
want = roundBits(ybits, prec, mode)
|
want = ybits.round(prec, mode)
|
||||||
if got.Cmp(want) != 0 {
|
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",
|
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)
|
i, prec, mode, z, zbits, x, xbits, got, want)
|
||||||
@ -1249,7 +1249,7 @@ func TestFloatQuo(t *testing.T) {
|
|||||||
|
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
// compute accurate (not rounded) result z
|
// compute accurate (not rounded) result z
|
||||||
bits := []int{preci - 1}
|
bits := Bits{preci - 1}
|
||||||
if i&3 != 0 {
|
if i&3 != 0 {
|
||||||
bits = append(bits, 0)
|
bits = append(bits, 0)
|
||||||
}
|
}
|
||||||
@ -1259,7 +1259,7 @@ func TestFloatQuo(t *testing.T) {
|
|||||||
if i&1 != 0 {
|
if i&1 != 0 {
|
||||||
bits = append(bits, -precf)
|
bits = append(bits, -precf)
|
||||||
}
|
}
|
||||||
z := fromBits(bits)
|
z := bits.Float()
|
||||||
|
|
||||||
// compute accurate x as z*y
|
// compute accurate x as z*y
|
||||||
y := new(Float).SetFloat64(3.14159265358979323e123)
|
y := new(Float).SetFloat64(3.14159265358979323e123)
|
||||||
@ -1280,7 +1280,7 @@ func TestFloatQuo(t *testing.T) {
|
|||||||
for d := -5; d < 5; d++ {
|
for d := -5; d < 5; d++ {
|
||||||
prec := uint(preci + d)
|
prec := uint(preci + d)
|
||||||
got := new(Float).SetPrec(prec).SetMode(mode).Quo(x, y)
|
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 {
|
if got.Cmp(want) != 0 {
|
||||||
t.Errorf("i = %d, prec = %d, %s:\n\t %s\n\t/ %s\n\t= %s\n\twant %s",
|
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)
|
i, prec, mode, x, y, got, want)
|
||||||
|
Loading…
Reference in New Issue
Block a user