Commit 2dfa4f4e authored by Robert Griesemer's avatar Robert Griesemer

math/big: use new nat.scan for Rat.SetString

Change-Id: Ida20bf95e8f0fdadb459c2daa6d22edae9c3ad16
Reviewed-on: https://go-review.googlesource.com/3091Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent dba9eb33
...@@ -626,6 +626,9 @@ func maxPow(b Word) (p Word, n int) { ...@@ -626,6 +626,9 @@ func maxPow(b Word) (p Word, n int) {
// pow returns x**n for n > 0, and 1 otherwise. // pow returns x**n for n > 0, and 1 otherwise.
func pow(x Word, n int) (p Word) { func pow(x Word, n int) (p Word) {
// n == sum of bi * 2**i, for 0 <= i < imax, and bi is 0 or 1
// thus x**n == product of x**(2**i) for all i where bi == 1
// (Russian Peasant Method for exponentiation)
p = 1 p = 1
for n > 0 { for n > 0 {
if n&1 != 0 { if n&1 != 0 {
......
...@@ -10,7 +10,9 @@ import ( ...@@ -10,7 +10,9 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io"
"math" "math"
"strconv"
"strings" "strings"
) )
...@@ -540,11 +542,11 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -540,11 +542,11 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
if len(s) == 0 { if len(s) == 0 {
return nil, false return nil, false
} }
// len(s) > 0
// check for a quotient // parse fraction a/b, if any
sep := strings.Index(s, "/") if sep := strings.Index(s, "/"); sep >= 0 {
if sep >= 0 { if _, ok := z.a.SetString(s[:sep], 10); !ok {
if _, ok := z.a.SetString(s[0:sep], 10); !ok {
return nil, false return nil, false
} }
s = s[sep+1:] s = s[sep+1:]
...@@ -558,38 +560,64 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -558,38 +560,64 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
return z.norm(), true return z.norm(), true
} }
// check for a decimal point // parse floating-point number
sep = strings.Index(s, ".")
// check for an exponent // parse sign
e := strings.IndexAny(s, "eE") var neg bool
var exp Int switch s[0] {
if e >= 0 { case '-':
if e < sep { neg = true
// The E must come after the decimal point. fallthrough
return nil, false case '+':
} s = s[1:]
if _, ok := exp.SetString(s[e+1:], 10); !ok { }
// 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 return nil, false
} }
s = s[0:e] s = s[:sep]
} }
if sep >= 0 {
s = s[0:sep] + s[sep+1:] // parse mantissa
exp.Sub(&exp, NewInt(int64(len(s)-sep))) 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 {
return nil, false
} }
if _, ok := z.a.SetString(s, 10); !ok { // there should be no unread characters left
if _, _, err = r.ReadRune(); err != io.EOF {
return nil, false return nil, false
} }
powTen := nat(nil).expNN(natTen, exp.abs, nil)
if exp.neg { // correct exponent
if ecorr < 0 {
exp += int64(ecorr)
}
// compute exponent factor
expabs := exp
if expabs < 0 {
expabs = -expabs
}
powTen := nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil)
// complete fraction
if exp < 0 {
z.b.abs = powTen z.b.abs = powTen
z.norm() z.norm()
} else { } else {
z.a.abs = z.a.abs.mul(z.a.abs, powTen) z.a.abs = z.a.abs.mul(z.a.abs, powTen)
z.b.abs = z.b.abs.make(0) z.b.abs = z.b.abs[:0]
} }
z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign
return z, true return z, true
} }
...@@ -667,7 +695,7 @@ func (x *Rat) GobEncode() ([]byte, error) { ...@@ -667,7 +695,7 @@ func (x *Rat) GobEncode() ([]byte, error) {
} }
buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4) buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4)
i := x.b.abs.bytes(buf) i := x.b.abs.bytes(buf)
j := x.a.abs.bytes(buf[0:i]) j := x.a.abs.bytes(buf[:i])
n := i - j n := i - j
if int(uint32(n)) != n { if int(uint32(n)) != n {
// this should never happen // this should never happen
......
...@@ -67,13 +67,13 @@ var setStringTests = []struct { ...@@ -67,13 +67,13 @@ var setStringTests = []struct {
{"1.", "1", true}, {"1.", "1", true},
{"1e0", "1", true}, {"1e0", "1", true},
{"1.e1", "10", true}, {"1.e1", "10", true},
{in: "1e", ok: false}, {in: "1e"},
{in: "1.e", ok: false}, {in: "1.e"},
{in: "1e+14e-5", ok: false}, {in: "1e+14e-5"},
{in: "1e4.5", ok: false}, {in: "1e4.5"},
{in: "r", ok: false}, {in: "r"},
{in: "a/b", ok: false}, {in: "a/b"},
{in: "a.b", ok: false}, {in: "a.b"},
{"-0.1", "-1/10", true}, {"-0.1", "-1/10", true},
{"-.1", "-1/10", true}, {"-.1", "-1/10", true},
{"2/4", "1/2", true}, {"2/4", "1/2", true},
...@@ -89,7 +89,7 @@ var setStringTests = []struct { ...@@ -89,7 +89,7 @@ var setStringTests = []struct {
{"53/70893980658822810696", "53/70893980658822810696", true}, {"53/70893980658822810696", "53/70893980658822810696", true},
{"106/141787961317645621392", "53/70893980658822810696", true}, {"106/141787961317645621392", "53/70893980658822810696", true},
{"204211327800791583.81095", "4084226556015831676219/20000", true}, {"204211327800791583.81095", "4084226556015831676219/20000", true},
{in: "1/0", ok: false}, {in: "1/0"},
} }
func TestRatSetString(t *testing.T) { func TestRatSetString(t *testing.T) {
...@@ -118,9 +118,9 @@ func TestRatScan(t *testing.T) { ...@@ -118,9 +118,9 @@ func TestRatScan(t *testing.T) {
_, err := fmt.Fscanf(&buf, "%v", x) _, err := fmt.Fscanf(&buf, "%v", x)
if err == nil != test.ok { if err == nil != test.ok {
if test.ok { if test.ok {
t.Errorf("#%d error: %s", i, err) t.Errorf("#%d (%s) error: %s", i, test.in, err)
} else { } else {
t.Errorf("#%d expected error", i) t.Errorf("#%d (%s) expected error", i, test.in)
} }
continue continue
} }
......
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