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) {
//
func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, error) {
// determine sign
ch, _, err := r.ReadRune()
neg, err := scanSign(r)
if err != nil {
return nil, 0, err
}
neg := false
switch ch {
case '-':
neg = true
case '+': // nothing to do
default:
r.UnreadRune()
}
// determine mantissa
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) {
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
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
......
......@@ -5,18 +5,22 @@
// Package big implements multi-precision arithmetic (big numbers).
// The following numeric types are supported:
//
// - Int signed integers
// - Rat rational numbers
// Int signed integers
// Rat rational numbers
// Float floating-point numbers
//
// 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
// is one of the operands it may be overwritten (and its memory reused).
// with T one of Int, Rat, or Float. For unary and binary operations, the
// 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
// returning a result other than *Int or *Rat take one of the operands as
// the receiver.
// returning a result other than *Int, *Rat, or *Float take an operand as
// the receiver (usually named x in that case).
//
package big
......@@ -1198,6 +1202,28 @@ func (x nat) bit(i uint) uint {
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 {
m := len(x)
n := len(y)
......
......@@ -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 {
// SetInt sets z to x (by making a copy of x) and returns z.
func (z *Rat) SetInt(x *Int) *Rat {
z.a.Set(x)
z.b.abs = z.b.abs.make(0)
z.b.abs = z.b.abs[:0]
return z
}
// SetInt64 sets z to x and returns z.
func (z *Rat) SetInt64(x int64) *Rat {
z.a.SetInt64(x)
z.b.abs = z.b.abs.make(0)
z.b.abs = z.b.abs[:0]
return z
}
......@@ -372,7 +372,7 @@ func (z *Rat) Inv(x *Rat) *Rat {
}
b := z.a.abs
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
return z
......@@ -417,12 +417,12 @@ func (z *Rat) norm() *Rat {
case len(z.a.abs) == 0:
// z == 0 - normalize sign and denominator
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:
// z is normalized int - nothing to do
case z.b.abs.cmp(natOne) == 0:
// z is int - normalize denominator
z.b.abs = z.b.abs.make(0)
z.b.abs = z.b.abs[:0]
default:
neg := z.a.neg
z.a.neg = false
......@@ -432,7 +432,7 @@ func (z *Rat) norm() *Rat {
z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs)
if z.b.abs.cmp(natOne) == 0 {
// z is int - normalize denominator
z.b.abs = z.b.abs.make(0)
z.b.abs = z.b.abs[:0]
}
}
z.a.neg = neg
......@@ -561,32 +561,26 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
}
// parse floating-point number
r := strings.NewReader(s)
// parse sign
var neg bool
switch s[0] {
case '-':
neg = true
fallthrough
case '+':
s = s[1:]
// sign
neg, err := scanSign(r)
if err != nil {
return nil, false
}
// parse exponent, if any
var exp int64
if sep := strings.IndexAny(s, "eE"); sep >= 0 {
var err error
if exp, err = strconv.ParseInt(s[sep+1:], 10, 64); err != nil {
return nil, false
}
s = s[:sep]
// mantissa
var ecorr int
z.a.abs, _, ecorr, err = z.a.abs.scan(r, 1)
if err != nil {
return nil, false
}
// parse mantissa
var err error
var ecorr int // exponent correction, valid if ecorr <= 0
r := strings.NewReader(s)
if z.a.abs, _, ecorr, err = z.a.abs.scan(r, 1); err != nil {
// exponent
var exp int64
var ebase int
exp, ebase, err = scanExponent(r)
if ebase == 2 || err != nil {
return nil, false
}
......@@ -600,7 +594,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
exp += int64(ecorr)
}
// compute exponent factor
// compute exponent power
expabs := exp
if expabs < 0 {
expabs = -expabs
......@@ -621,6 +615,64 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
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).
func (x *Rat) String() string {
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