Commit 9f22de7a authored by Robert Griesemer's avatar Robert Griesemer

math/big: parsing of fractions and floats in mantissa bases other than 10

Change-Id: I1eaebf956a69e0958201cc5e0a9beefa062c71e1
Reviewed-on: https://go-review.googlesource.com/3454Reviewed-by: 's avatarAlan Donovan <adonovan@google.com>
parent da167b00
This diff is collapsed.
...@@ -79,7 +79,7 @@ func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) { ...@@ -79,7 +79,7 @@ func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) {
// TestFloatRound tests basic rounding. // TestFloatRound tests basic rounding.
func TestFloatRound(t *testing.T) { func TestFloatRound(t *testing.T) {
var tests = []struct { for _, test := range []struct {
prec uint prec uint
x, zero, neven, naway, away string // input, results rounded to prec bits x, zero, neven, naway, away string // input, results rounded to prec bits
}{ }{
...@@ -154,9 +154,7 @@ func TestFloatRound(t *testing.T) { ...@@ -154,9 +154,7 @@ func TestFloatRound(t *testing.T) {
{1, "1101001", "1000000", "10000000", "10000000", "10000000"}, {1, "1101001", "1000000", "10000000", "10000000", "10000000"},
{1, "1110001", "1000000", "10000000", "10000000", "10000000"}, {1, "1110001", "1000000", "10000000", "10000000", "10000000"},
{1, "1111001", "1000000", "10000000", "10000000", "10000000"}, {1, "1111001", "1000000", "10000000", "10000000", "10000000"},
} } {
for _, test := range tests {
x := fromBinary(test.x) x := fromBinary(test.x)
z := fromBinary(test.zero) z := fromBinary(test.zero)
e := fromBinary(test.neven) e := fromBinary(test.neven)
...@@ -195,7 +193,7 @@ func TestFloatRound24(t *testing.T) { ...@@ -195,7 +193,7 @@ func TestFloatRound24(t *testing.T) {
} }
func TestFloatSetUint64(t *testing.T) { func TestFloatSetUint64(t *testing.T) {
var tests = []uint64{ for _, want := range []uint64{
0, 0,
1, 1,
2, 2,
...@@ -204,8 +202,7 @@ func TestFloatSetUint64(t *testing.T) { ...@@ -204,8 +202,7 @@ func TestFloatSetUint64(t *testing.T) {
1<<32 - 1, 1<<32 - 1,
1 << 32, 1 << 32,
1<<64 - 1, 1<<64 - 1,
} } {
for _, want := range tests {
f := new(Float).SetUint64(want) f := new(Float).SetUint64(want)
if got := f.Uint64(); got != want { if got := f.Uint64(); got != want {
t.Errorf("got %d (%s); want %d", got, f.pstring(), want) t.Errorf("got %d (%s); want %d", got, f.pstring(), want)
...@@ -214,7 +211,7 @@ func TestFloatSetUint64(t *testing.T) { ...@@ -214,7 +211,7 @@ func TestFloatSetUint64(t *testing.T) {
} }
func TestFloatSetInt64(t *testing.T) { func TestFloatSetInt64(t *testing.T) {
var tests = []int64{ for _, want := range []int64{
0, 0,
1, 1,
2, 2,
...@@ -223,8 +220,7 @@ func TestFloatSetInt64(t *testing.T) { ...@@ -223,8 +220,7 @@ func TestFloatSetInt64(t *testing.T) {
1<<32 - 1, 1<<32 - 1,
1 << 32, 1 << 32,
1<<63 - 1, 1<<63 - 1,
} } {
for _, want := range tests {
for i := range [2]int{} { for i := range [2]int{} {
if i&1 != 0 { if i&1 != 0 {
want = -want want = -want
...@@ -238,7 +234,7 @@ func TestFloatSetInt64(t *testing.T) { ...@@ -238,7 +234,7 @@ func TestFloatSetInt64(t *testing.T) {
} }
func TestFloatSetFloat64(t *testing.T) { func TestFloatSetFloat64(t *testing.T) {
var tests = []float64{ for _, want := range []float64{
0, 0,
1, 1,
2, 2,
...@@ -248,8 +244,7 @@ func TestFloatSetFloat64(t *testing.T) { ...@@ -248,8 +244,7 @@ func TestFloatSetFloat64(t *testing.T) {
3.14159265e10, 3.14159265e10,
2.718281828e-123, 2.718281828e-123,
1.0 / 3, 1.0 / 3,
} } {
for _, want := range tests {
for i := range [2]int{} { for i := range [2]int{} {
if i&1 != 0 { if i&1 != 0 {
want = -want want = -want
...@@ -396,7 +391,7 @@ func TestFloatMul(t *testing.T) { ...@@ -396,7 +391,7 @@ func TestFloatMul(t *testing.T) {
// TestFloatMul64 tests that Float.Mul/Quo of numbers with // TestFloatMul64 tests that Float.Mul/Quo of numbers with
// 53bit mantissa behaves like float64 multiplication/division. // 53bit mantissa behaves like float64 multiplication/division.
func TestFloatMul64(t *testing.T) { func TestFloatMul64(t *testing.T) {
var tests = []struct { for _, test := range []struct {
x, y float64 x, y float64
}{ }{
{0, 0}, {0, 0},
...@@ -407,8 +402,7 @@ func TestFloatMul64(t *testing.T) { ...@@ -407,8 +402,7 @@ func TestFloatMul64(t *testing.T) {
{2.718281828, 3.14159265358979}, {2.718281828, 3.14159265358979},
{2.718281828e10, 3.14159265358979e-32}, {2.718281828e10, 3.14159265358979e-32},
{1.0 / 3, 1e200}, {1.0 / 3, 1e200},
} } {
for _, test := range tests {
for i := range [8]int{} { for i := range [8]int{} {
x0, y0 := test.x, test.y x0, y0 := test.x, test.y
if i&1 != 0 { if i&1 != 0 {
...@@ -552,7 +546,7 @@ func normBits(x []int) []int { ...@@ -552,7 +546,7 @@ func normBits(x []int) []int {
} }
func TestNormBits(t *testing.T) { func TestNormBits(t *testing.T) {
var tests = []struct { for _, test := range []struct {
x, want []int x, want []int
}{ }{
{nil, nil}, {nil, nil},
...@@ -561,9 +555,7 @@ func TestNormBits(t *testing.T) { ...@@ -561,9 +555,7 @@ func TestNormBits(t *testing.T) {
{[]int{0, 0}, []int{1}}, {[]int{0, 0}, []int{1}},
{[]int{3, 1, 1}, []int{2, 3}}, {[]int{3, 1, 1}, []int{2, 3}},
{[]int{10, 9, 8, 7, 6, 6}, []int{11}}, {[]int{10, 9, 8, 7, 6, 6}, []int{11}},
} } {
for _, test := range tests {
got := fmt.Sprintf("%v", normBits(test.x)) got := fmt.Sprintf("%v", normBits(test.x))
want := fmt.Sprintf("%v", test.want) want := fmt.Sprintf("%v", test.want)
if got != want { if got != want {
...@@ -665,27 +657,25 @@ func fromBits(bits ...int) *Float { ...@@ -665,27 +657,25 @@ func fromBits(bits ...int) *Float {
} }
func TestFromBits(t *testing.T) { func TestFromBits(t *testing.T) {
var tests = []struct { for _, test := range []struct {
bits []int bits []int
want string want string
}{ }{
// all different bit numbers // all different bit numbers
{nil, "0"}, {nil, "0"},
{[]int{0}, "0.8p1"}, {[]int{0}, "0x.8p1"},
{[]int{1}, "0.8p2"}, {[]int{1}, "0x.8p2"},
{[]int{-1}, "0.8p0"}, {[]int{-1}, "0x.8p0"},
{[]int{63}, "0.8p64"}, {[]int{63}, "0x.8p64"},
{[]int{33, -30}, "0.8000000000000001p34"}, {[]int{33, -30}, "0x.8000000000000001p34"},
{[]int{255, 0}, "0.8000000000000000000000000000000000000000000000000000000000000001p256"}, {[]int{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p256"},
// multiple equal bit numbers // multiple equal bit numbers
{[]int{0, 0}, "0.8p2"}, {[]int{0, 0}, "0x.8p2"},
{[]int{0, 0, 0, 0}, "0.8p3"}, {[]int{0, 0, 0, 0}, "0x.8p3"},
{[]int{0, 1, 0}, "0.8p3"}, {[]int{0, 1, 0}, "0x.8p3"},
{append([]int{2, 1, 0} /* 7 */, []int{3, 1} /* 10 */ ...), "0.88p5" /* 17 */}, {append([]int{2, 1, 0} /* 7 */, []int{3, 1} /* 10 */ ...), "0x.88p5" /* 17 */},
} } {
for _, test := range tests {
f := fromBits(test.bits...) f := fromBits(test.bits...)
if got := f.pstring(); got != test.want { if got := f.pstring(); 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)
...@@ -757,19 +747,39 @@ func TestFloatSetFloat64String(t *testing.T) { ...@@ -757,19 +747,39 @@ func TestFloatSetFloat64String(t *testing.T) {
} }
} }
func TestFloatpstring(t *testing.T) { func TestFloatFormat(t *testing.T) {
var tests = []struct { for _, test := range []struct {
x Float x string
want string format byte
prec int
want string
}{ }{
{Float{}, "0"}, {"0", 'b', 0, "0"},
{Float{neg: true}, "-0"}, {"-0", 'b', 0, "-0"},
{Float{mant: nat{0x87654321}}, "0.87654321p0"}, {"1.0", 'b', 0, "4503599627370496p1"},
{Float{mant: nat{0x87654321}, exp: -10}, "0.87654321p-10"}, {"-1.0", 'b', 0, "-4503599627370496p1"},
}
for _, test := range tests { {"0", 'p', 0, "0"},
if got := test.x.pstring(); got != test.want { {"-0", 'p', 0, "-0"},
t.Errorf("%v: got %s; want %s", test.x, got, test.want) {"1024.0", 'p', 0, "0x.8p11"},
{"-1024.0", 'p', 0, "-0x.8p11"},
} {
f64, err := strconv.ParseFloat(test.x, 64)
if err != nil {
t.Error(err)
continue
}
f := new(Float).SetFloat64(f64)
got := f.Format(test.format, test.prec)
if got != test.want {
t.Errorf("%v: got %s", test, got)
}
if test.format == 'b' || test.format == 'p' {
continue // 'b', 'p' format not supported or different in strconv.Format
}
want := strconv.FormatFloat(f64, test.format, test.prec, 64)
if got != want {
t.Errorf("%v: got %s; want %s", test, got, want)
} }
} }
} }
...@@ -469,7 +469,7 @@ func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) { ...@@ -469,7 +469,7 @@ func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) {
} }
// determine mantissa // determine mantissa
z.abs, base, _, err = z.abs.scan(r, base) z.abs, base, _, err = z.abs.scan(r, base, false)
if err != nil { if err != nil {
return nil, base, err return nil, base, err
} }
......
...@@ -649,28 +649,32 @@ func pow(x Word, n int) (p Word) { ...@@ -649,28 +649,32 @@ func pow(x Word, n int) (p Word) {
// It returns the corresponding natural number res, the actual base b, // It returns the corresponding natural number res, the actual base b,
// a digit count, and an error err, if any. // a digit count, and an error err, if any.
// //
// number = [ prefix ] digits | digits "." [ digits ] | "." digits . // number = [ prefix ] mantissa .
// prefix = "0" [ "x" | "X" | "b" | "B" ] . // prefix = "0" [ "x" | "X" | "b" | "B" ] .
// digits = digit { digit } . // mantissa = digits | digits "." [ digits ] | "." digits .
// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . // digits = digit { digit } .
// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
//
// The base argument must be 0 or a value between 0 through MaxBase.
// //
// The base argument must be a value between 0 and MaxBase (inclusive).
// For base 0, the number prefix determines the actual base: A prefix of // For base 0, the number prefix determines the actual base: A prefix of
// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and // ``0x'' or ``0X'' selects base 16; if fracOk is not set, the ``0'' prefix
// a ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base // selects base 8, and a ``0b'' or ``0B'' prefix selects base 2. Otherwise
// is 10 and no prefix is permitted. // the selected base is 10 and no prefix is permitted.
// //
// Base argument 1 selects actual base 10 but also enables scanning a number // If fracOk is set, an octal prefix is ignored (a leading ``0'' simply
// with a decimal point. // stands for a zero digit), and a period followed by a fractional part
// is permitted. The result value is computed as if there were no period
// present; and the count value is used to determine the fractional part.
// //
// A result digit count > 0 corresponds to the number of (non-prefix) digits // A result digit count > 0 corresponds to the number of (non-prefix) digits
// parsed. A digit count <= 0 indicates the presence of a decimal point (for // parsed. A digit count <= 0 indicates the presence of a period (if fracOk
// base == 1, only), and the number of fractional digits is -count. In this // is set, only), and -count is the number of fractional digits found.
// case, the value of the scanned number is res * 10**count. // In this case, the value of the scanned number is res * 10**count.
// //
func (z nat) scan(r io.ByteScanner, base int) (res nat, b, count int, err error) { func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) {
// reject illegal bases // reject illegal bases
if base < 0 || base > MaxBase { if base != 0 && base < 2 || base > MaxBase {
err = errors.New("illegal number base") err = errors.New("illegal number base")
return return
} }
...@@ -682,31 +686,37 @@ func (z nat) scan(r io.ByteScanner, base int) (res nat, b, count int, err error) ...@@ -682,31 +686,37 @@ func (z nat) scan(r io.ByteScanner, base int) (res nat, b, count int, err error)
} }
// determine actual base // determine actual base
switch base { b = base
case 0: if base == 0 {
// actual base is 10 unless there's a base prefix // actual base is 10 unless there's a base prefix
b = 10 b = 10
if ch == '0' { if ch == '0' {
count = 1
switch ch, err = r.ReadByte(); err { switch ch, err = r.ReadByte(); err {
case nil: case nil:
// possibly one of 0x, 0X, 0b, 0B // possibly one of 0x, 0X, 0b, 0B
b = 8 if !fracOk {
b = 8
}
switch ch { switch ch {
case 'x', 'X': case 'x', 'X':
b = 16 b = 16
case 'b', 'B': case 'b', 'B':
b = 2 b = 2
} }
if b == 2 || b == 16 { switch b {
case 16, 2:
count = 0 // prefix is not counted
if ch, err = r.ReadByte(); err != nil { if ch, err = r.ReadByte(); err != nil {
// io.EOF is also an error in this case // io.EOF is also an error in this case
return return
} }
case 8:
count = 0 // prefix is not counted
} }
case io.EOF: case io.EOF:
// input is "0" // input is "0"
res = z[:0] res = z[:0]
count = 1
err = nil err = nil
return return
default: default:
...@@ -714,11 +724,6 @@ func (z nat) scan(r io.ByteScanner, base int) (res nat, b, count int, err error) ...@@ -714,11 +724,6 @@ func (z nat) scan(r io.ByteScanner, base int) (res nat, b, count int, err error)
return return
} }
} }
case 1:
// actual base is 10 and decimal point is permitted
b = 10
default:
b = base
} }
// convert string // convert string
...@@ -732,8 +737,8 @@ func (z nat) scan(r io.ByteScanner, base int) (res nat, b, count int, err error) ...@@ -732,8 +737,8 @@ func (z nat) scan(r io.ByteScanner, base int) (res nat, b, count int, err error)
i := 0 // 0 <= i < n i := 0 // 0 <= i < n
dp := -1 // position of decimal point dp := -1 // position of decimal point
for { for {
if base == 1 && ch == '.' { if fracOk && ch == '.' {
base = 10 // no 2nd decimal point permitted fracOk = false
dp = count dp = count
// advance // advance
if ch, err = r.ReadByte(); err != nil { if ch, err = r.ReadByte(); err != nil {
......
...@@ -88,7 +88,7 @@ var prodNN = []argNN{ ...@@ -88,7 +88,7 @@ var prodNN = []argNN{
} }
func natFromString(s string) nat { func natFromString(s string) nat {
x, _, _, err := nat(nil).scan(strings.NewReader(s), 0) x, _, _, err := nat(nil).scan(strings.NewReader(s), 0, false)
if err != nil { if err != nil {
panic(err) panic(err)
} }
...@@ -271,7 +271,7 @@ func TestString(t *testing.T) { ...@@ -271,7 +271,7 @@ func TestString(t *testing.T) {
t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s)
} }
x, b, _, err := nat(nil).scan(strings.NewReader(a.s), len(a.c)) x, b, _, err := nat(nil).scan(strings.NewReader(a.s), len(a.c), false)
if x.cmp(a.x) != 0 { if x.cmp(a.x) != 0 {
t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
} }
...@@ -287,6 +287,7 @@ func TestString(t *testing.T) { ...@@ -287,6 +287,7 @@ func TestString(t *testing.T) {
var natScanTests = []struct { var natScanTests = []struct {
s string // string to be scanned s string // string to be scanned
base int // input base base int // input base
frac bool // fraction ok
x nat // expected nat x nat // expected nat
b int // expected base b int // expected base
count int // expected digit count count int // expected digit count
...@@ -313,39 +314,39 @@ var natScanTests = []struct { ...@@ -313,39 +314,39 @@ var natScanTests = []struct {
{s: "0x.0"}, {s: "0x.0"},
// no errors // no errors
{"0", 0, nil, 10, 1, true, 0}, {"0", 0, false, nil, 10, 1, true, 0},
{"0", 10, nil, 10, 1, true, 0}, {"0", 10, false, nil, 10, 1, true, 0},
{"0", 36, nil, 36, 1, true, 0}, {"0", 36, false, nil, 36, 1, true, 0},
{"1", 0, nat{1}, 10, 1, true, 0}, {"1", 0, false, nat{1}, 10, 1, true, 0},
{"1", 10, nat{1}, 10, 1, true, 0}, {"1", 10, false, nat{1}, 10, 1, true, 0},
{"0 ", 0, nil, 10, 1, true, ' '}, {"0 ", 0, false, nil, 10, 1, true, ' '},
{"08", 0, nil, 10, 1, true, '8'}, {"08", 0, false, nil, 10, 1, true, '8'},
{"08", 10, nat{8}, 10, 2, true, 0}, {"08", 10, false, nat{8}, 10, 2, true, 0},
{"018", 0, nat{1}, 8, 1, true, '8'}, {"018", 0, false, nat{1}, 8, 1, true, '8'},
{"0b1", 0, nat{1}, 2, 1, true, 0}, {"0b1", 0, false, nat{1}, 2, 1, true, 0},
{"0b11000101", 0, nat{0xc5}, 2, 8, true, 0}, {"0b11000101", 0, false, nat{0xc5}, 2, 8, true, 0},
{"03271", 0, nat{03271}, 8, 4, true, 0}, {"03271", 0, false, nat{03271}, 8, 4, true, 0},
{"10ab", 0, nat{10}, 10, 2, true, 'a'}, {"10ab", 0, false, nat{10}, 10, 2, true, 'a'},
{"1234567890", 0, nat{1234567890}, 10, 10, true, 0}, {"1234567890", 0, false, nat{1234567890}, 10, 10, true, 0},
{"xyz", 36, nat{(33*36+34)*36 + 35}, 36, 3, true, 0}, {"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, 0},
{"xyz?", 36, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'}, {"xyz?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'},
{"0x", 16, nil, 16, 1, true, 'x'}, {"0x", 16, false, nil, 16, 1, true, 'x'},
{"0xdeadbeef", 0, nat{0xdeadbeef}, 16, 8, true, 0}, {"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
{"0XDEADBEEF", 0, nat{0xdeadbeef}, 16, 8, true, 0}, {"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
// no errors, decimal point // no errors, decimal point
{"0.", 0, nil, 10, 1, true, '.'}, {"0.", 0, false, nil, 10, 1, true, '.'},
{"0.", 1, nil, 10, 0, true, 0}, {"0.", 10, true, nil, 10, 0, true, 0},
{"0.1.2", 1, nat{1}, 10, -1, true, '.'}, {"0.1.2", 10, true, nat{1}, 10, -1, true, '.'},
{".000", 1, nil, 10, -3, true, 0}, {".000", 10, true, nil, 10, -3, true, 0},
{"12.3", 1, nat{123}, 10, -1, true, 0}, {"12.3", 10, true, nat{123}, 10, -1, true, 0},
{"012.345", 1, nat{12345}, 10, -3, true, 0}, {"012.345", 10, true, nat{12345}, 10, -3, true, 0},
} }
func TestScanBase(t *testing.T) { func TestScanBase(t *testing.T) {
for _, a := range natScanTests { for _, a := range natScanTests {
r := strings.NewReader(a.s) r := strings.NewReader(a.s)
x, b, count, err := nat(nil).scan(r, a.base) x, b, count, err := nat(nil).scan(r, a.base, a.frac)
if err == nil && !a.ok { if err == nil && !a.ok {
t.Errorf("scan%+v\n\texpected error", a) t.Errorf("scan%+v\n\texpected error", a)
} }
...@@ -431,7 +432,7 @@ var pi = "3" + ...@@ -431,7 +432,7 @@ var pi = "3" +
// Test case for BenchmarkScanPi. // Test case for BenchmarkScanPi.
func TestScanPi(t *testing.T) { func TestScanPi(t *testing.T) {
var x nat var x nat
z, _, _, err := x.scan(strings.NewReader(pi), 10) z, _, _, err := x.scan(strings.NewReader(pi), 10, false)
if err != nil { if err != nil {
t.Errorf("scanning pi: %s", err) t.Errorf("scanning pi: %s", err)
} }
...@@ -457,13 +458,13 @@ func TestScanPiParallel(t *testing.T) { ...@@ -457,13 +458,13 @@ func TestScanPiParallel(t *testing.T) {
func BenchmarkScanPi(b *testing.B) { func BenchmarkScanPi(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
var x nat var x nat
x.scan(strings.NewReader(pi), 10) x.scan(strings.NewReader(pi), 10, false)
} }
} }
func BenchmarkStringPiParallel(b *testing.B) { func BenchmarkStringPiParallel(b *testing.B) {
var x nat var x nat
x, _, _, _ = x.scan(strings.NewReader(pi), 0) x, _, _, _ = x.scan(strings.NewReader(pi), 0, false)
if x.decimalString() != pi { if x.decimalString() != pi {
panic("benchmark incorrect: conversion failed") panic("benchmark incorrect: conversion failed")
} }
...@@ -511,7 +512,7 @@ func ScanHelper(b *testing.B, base int, x, y Word) { ...@@ -511,7 +512,7 @@ func ScanHelper(b *testing.B, base int, x, y Word) {
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
z.scan(strings.NewReader(s), base) z.scan(strings.NewReader(s), base, false)
} }
} }
......
...@@ -546,12 +546,12 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -546,12 +546,12 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
// parse fraction a/b, if any // parse fraction a/b, if any
if sep := strings.Index(s, "/"); sep >= 0 { if sep := strings.Index(s, "/"); sep >= 0 {
if _, ok := z.a.SetString(s[:sep], 10); !ok { if _, ok := z.a.SetString(s[:sep], 0); !ok {
return nil, false return nil, false
} }
s = s[sep+1:] s = s[sep+1:]
var err error var err error
if z.b.abs, _, _, err = z.b.abs.scan(strings.NewReader(s), 10); err != nil { if z.b.abs, _, _, err = z.b.abs.scan(strings.NewReader(s), 0, false); err != nil {
return nil, false return nil, false
} }
if len(z.b.abs) == 0 { if len(z.b.abs) == 0 {
...@@ -571,7 +571,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) { ...@@ -571,7 +571,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
// mantissa // mantissa
var ecorr int var ecorr int
z.a.abs, _, ecorr, err = z.a.abs.scan(r, 1) z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true)
if err != nil { if err != nil {
return nil, false return nil, false
} }
......
...@@ -56,10 +56,12 @@ func TestZeroRat(t *testing.T) { ...@@ -56,10 +56,12 @@ func TestZeroRat(t *testing.T) {
z.Quo(&x, &y) z.Quo(&x, &y)
} }
var setStringTests = []struct { type StringTest struct {
in, out string in, out string
ok bool ok bool
}{ }
var setStringTests = []StringTest{
{"0", "0", true}, {"0", "0", true},
{"-0", "0", true}, {"-0", "0", true},
{"1", "1", true}, {"1", "1", true},
...@@ -92,8 +94,22 @@ var setStringTests = []struct { ...@@ -92,8 +94,22 @@ var setStringTests = []struct {
{in: "1/0"}, {in: "1/0"},
} }
// These are not supported by fmt.Fscanf.
var setStringTests2 = []StringTest{
{"0x10", "16", true},
{"-010/1", "-8", true}, // TODO(gri) should we even permit octal here?
{"-010.", "-10", true},
{"0x10/0x20", "1/2", true},
{"0b1000/3", "8/3", true},
// TODO(gri) add more tests
}
func TestRatSetString(t *testing.T) { func TestRatSetString(t *testing.T) {
for i, test := range setStringTests { var tests []StringTest
tests = append(tests, setStringTests...)
tests = append(tests, setStringTests2...)
for i, test := range tests {
x, ok := new(Rat).SetString(test.in) x, ok := new(Rat).SetString(test.in)
if ok { if ok {
......
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