Commit bd275b23 authored by Robert Griesemer's avatar Robert Griesemer

math/big: multi-precision Floats (starting point)

Implemented:
- +, -, *, /, and some unary ops
- all rounding modes
- basic conversions
- string to float conversion
- tests

Missing:
- float to string conversion, formatting
- handling of +/-0 and +/-inf (under- and overflow)
- various TODOs and cleanups

With precision set to 24 or 53, the results match
float32 or float64 operations exactly (excluding
NaNs and denormalized numbers which will not be
supported).

Change-Id: I3121e90fc4b1528e40bb6ff526008da18b3c6520
Reviewed-on: https://go-review.googlesource.com/1218Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent 571d02d9
This diff is collapsed.
This diff is collapsed.
...@@ -463,18 +463,10 @@ func (x *Int) Format(s fmt.State, ch rune) { ...@@ -463,18 +463,10 @@ func (x *Int) Format(s fmt.State, ch rune) {
// //
func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, error) { func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, error) {
// determine sign // determine sign
ch, _, err := r.ReadRune() neg, err := scanSign(r)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
neg := false
switch ch {
case '-':
neg = true
case '+': // nothing to do
default:
r.UnreadRune()
}
// determine mantissa // determine mantissa
z.abs, base, _, err = z.abs.scan(r, base) z.abs, base, _, err = z.abs.scan(r, base)
...@@ -486,6 +478,22 @@ func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, error) { ...@@ -486,6 +478,22 @@ func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, error) {
return z, base, nil return z, base, nil
} }
func scanSign(r io.RuneScanner) (neg bool, err error) {
var ch rune
if ch, _, err = r.ReadRune(); err != nil {
return false, err
}
switch ch {
case '-':
neg = true
case '+':
// nothing to do
default:
r.UnreadRune()
}
return
}
// Scan is a support routine for fmt.Scanner; it sets z to the value of // Scan is a support routine for fmt.Scanner; it sets z to the value of
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal), // the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). // 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
......
...@@ -5,18 +5,22 @@ ...@@ -5,18 +5,22 @@
// Package big implements multi-precision arithmetic (big numbers). // Package big implements multi-precision arithmetic (big numbers).
// The following numeric types are supported: // The following numeric types are supported:
// //
// - Int signed integers // Int signed integers
// - Rat rational numbers // Rat rational numbers
// Float floating-point numbers
// //
// Methods are typically of the form: // Methods are typically of the form:
// //
// func (z *Int) Op(x, y *Int) *Int (similar for *Rat) // func (z *T) Unary(x *T) *T // z = op x
// func (z *T) Binary(x, y *T) *T // z = x op y
// func (x *T) M() T1 // v = x.M()
// //
// and implement operations z = x Op y with the result as receiver; if it // with T one of Int, Rat, or Float. For unary and binary operations, the
// is one of the operands it may be overwritten (and its memory reused). // result is the receiver (usually named z in that case); if it is one of
// the operands x or y it may be overwritten (and its memory reused).
// To enable chaining of operations, the result is also returned. Methods // To enable chaining of operations, the result is also returned. Methods
// returning a result other than *Int or *Rat take one of the operands as // returning a result other than *Int, *Rat, or *Float take an operand as
// the receiver. // the receiver (usually named x in that case).
// //
package big package big
...@@ -1198,6 +1202,28 @@ func (x nat) bit(i uint) uint { ...@@ -1198,6 +1202,28 @@ func (x nat) bit(i uint) uint {
return uint(x[j] >> (i % _W) & 1) return uint(x[j] >> (i % _W) & 1)
} }
// sticky returns 1 if there's a 1 bit within the
// i least significant bits, otherwise it returns 0.
func (x nat) sticky(i uint) uint {
j := i / _W
if j >= uint(len(x)) {
if len(x) == 0 {
return 0
}
return 1
}
// 0 <= j < len(x)
for _, x := range x[:j] {
if x != 0 {
return 1
}
}
if x[j]<<(_W-i%_W) != 0 {
return 1
}
return 0
}
func (z nat) and(x, y nat) nat { func (z nat) and(x, y nat) nat {
m := len(x) m := len(x)
n := len(y) n := len(y)
......
...@@ -894,3 +894,43 @@ func TestBit(t *testing.T) { ...@@ -894,3 +894,43 @@ func TestBit(t *testing.T) {
} }
} }
} }
var stickyTests = []struct {
x string
i uint
want uint
}{
{"0", 0, 0},
{"0", 1, 0},
{"0", 1000, 0},
{"0x1", 0, 0},
{"0x1", 1, 1},
{"0x1350", 0, 0},
{"0x1350", 4, 0},
{"0x1350", 5, 1},
{"0x8000000000000000", 63, 0},
{"0x8000000000000000", 64, 1},
{"0x1" + strings.Repeat("0", 100), 400, 0},
{"0x1" + strings.Repeat("0", 100), 401, 1},
}
func TestSticky(t *testing.T) {
for i, test := range stickyTests {
x := natFromString(test.x)
if got := x.sticky(test.i); got != test.want {
t.Errorf("#%d: %s.sticky(%d) = %v; want %v", i, test.x, test.i, got, test.want)
}
if test.want == 1 {
// all subsequent i's should also return 1
for d := uint(1); d <= 3; d++ {
if got := x.sticky(test.i + d); got != 1 {
t.Errorf("#%d: %s.sticky(%d) = %v; want %v", i, test.x, test.i+d, got, 1)
}
}
}
}
}
...@@ -326,14 +326,14 @@ func (z *Rat) SetFrac64(a, b int64) *Rat { ...@@ -326,14 +326,14 @@ func (z *Rat) SetFrac64(a, b int64) *Rat {
// SetInt sets z to x (by making a copy of x) and returns z. // SetInt sets z to x (by making a copy of x) and returns z.
func (z *Rat) SetInt(x *Int) *Rat { func (z *Rat) SetInt(x *Int) *Rat {
z.a.Set(x) z.a.Set(x)
z.b.abs = z.b.abs.make(0) z.b.abs = z.b.abs[:0]
return z return z
} }
// SetInt64 sets z to x and returns z. // SetInt64 sets z to x and returns z.
func (z *Rat) SetInt64(x int64) *Rat { func (z *Rat) SetInt64(x int64) *Rat {
z.a.SetInt64(x) z.a.SetInt64(x)
z.b.abs = z.b.abs.make(0) z.b.abs = z.b.abs[:0]
return z return z
} }
...@@ -372,7 +372,7 @@ func (z *Rat) Inv(x *Rat) *Rat { ...@@ -372,7 +372,7 @@ func (z *Rat) Inv(x *Rat) *Rat {
} }
b := z.a.abs b := z.a.abs
if b.cmp(natOne) == 0 { if b.cmp(natOne) == 0 {
b = b.make(0) // normalize denominator b = b[:0] // normalize denominator
} }
z.a.abs, z.b.abs = a, b // sign doesn't change z.a.abs, z.b.abs = a, b // sign doesn't change
return z return z
...@@ -417,12 +417,12 @@ func (z *Rat) norm() *Rat { ...@@ -417,12 +417,12 @@ func (z *Rat) norm() *Rat {
case len(z.a.abs) == 0: case len(z.a.abs) == 0:
// z == 0 - normalize sign and denominator // z == 0 - normalize sign and denominator
z.a.neg = false z.a.neg = false
z.b.abs = z.b.abs.make(0) z.b.abs = z.b.abs[:0]
case len(z.b.abs) == 0: case len(z.b.abs) == 0:
// z is normalized int - nothing to do // z is normalized int - nothing to do
case z.b.abs.cmp(natOne) == 0: case z.b.abs.cmp(natOne) == 0:
// z is int - normalize denominator // z is int - normalize denominator
z.b.abs = z.b.abs.make(0) z.b.abs = z.b.abs[:0]
default: default:
neg := z.a.neg neg := z.a.neg
z.a.neg = false z.a.neg = false
...@@ -432,7 +432,7 @@ func (z *Rat) norm() *Rat { ...@@ -432,7 +432,7 @@ func (z *Rat) norm() *Rat {
z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs) z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs)
if z.b.abs.cmp(natOne) == 0 { if z.b.abs.cmp(natOne) == 0 {
// z is int - normalize denominator // z is int - normalize denominator
z.b.abs = z.b.abs.make(0) z.b.abs = z.b.abs[:0]
} }
} }
z.a.neg = neg z.a.neg = neg
...@@ -561,32 +561,26 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -561,32 +561,26 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
} }
// parse floating-point number // parse floating-point number
r := strings.NewReader(s)
// parse sign // sign
var neg bool neg, err := scanSign(r)
switch s[0] { if err != nil {
case '-': return nil, false
neg = true
fallthrough
case '+':
s = s[1:]
} }
// parse exponent, if any // mantissa
var exp int64 var ecorr int
if sep := strings.IndexAny(s, "eE"); sep >= 0 { z.a.abs, _, ecorr, err = z.a.abs.scan(r, 1)
var err error if err != nil {
if exp, err = strconv.ParseInt(s[sep+1:], 10, 64); err != nil { return nil, false
return nil, false
}
s = s[:sep]
} }
// parse mantissa // exponent
var err error var exp int64
var ecorr int // exponent correction, valid if ecorr <= 0 var ebase int
r := strings.NewReader(s) exp, ebase, err = scanExponent(r)
if z.a.abs, _, ecorr, err = z.a.abs.scan(r, 1); err != nil { if ebase == 2 || err != nil {
return nil, false return nil, false
} }
...@@ -600,7 +594,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -600,7 +594,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
exp += int64(ecorr) exp += int64(ecorr)
} }
// compute exponent factor // compute exponent power
expabs := exp expabs := exp
if expabs < 0 { if expabs < 0 {
expabs = -expabs expabs = -expabs
...@@ -621,6 +615,64 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -621,6 +615,64 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
return z, true return z, true
} }
func scanExponent(r io.RuneScanner) (exp int64, base int, err error) {
base = 10
var ch rune
if ch, _, err = r.ReadRune(); err != nil {
if err == io.EOF {
err = nil // no exponent; same as e0
}
return
}
switch ch {
case 'e', 'E':
// ok
case 'p':
base = 2
default:
r.UnreadRune()
return // no exponent; same as e0
}
var neg bool
if neg, err = scanSign(r); err != nil {
return
}
var digits []byte
if neg {
digits = append(digits, '-')
}
// no need to use nat.scan for exponent digits
// since we only care about int64 values - the
// from-scratch scan is easy enough and faster
for i := 0; ; i++ {
if ch, _, err = r.ReadRune(); err != nil {
if err != io.EOF || i == 0 {
return
}
err = nil
break // i > 0
}
if ch < '0' || '9' < ch {
if i == 0 {
r.UnreadRune()
err = fmt.Errorf("invalid exponent (missing digits)")
return
}
break // i > 0
}
digits = append(digits, byte(ch))
}
// i > 0 => we have at least one digit
exp, err = strconv.ParseInt(string(digits), 10, 64)
return
}
// String returns a string representation of x in the form "a/b" (even if b == 1). // String returns a string representation of x in the form "a/b" (even if b == 1).
func (x *Rat) String() string { func (x *Rat) String() string {
s := "/1" s := "/1"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment