]> Cypherpunks.ru repositories - gostls13.git/commitdiff
math/big: split rat conversion routines and tests into separate files
authorRobert Griesemer <gri@golang.org>
Fri, 30 Jan 2015 22:20:04 +0000 (14:20 -0800)
committerRobert Griesemer <gri@golang.org>
Fri, 30 Jan 2015 23:02:51 +0000 (23:02 +0000)
No other functional changes.

Change-Id: I8be1fc488caa4f3d4c00afcb8c00475bfcd10709
Reviewed-on: https://go-review.googlesource.com/3673
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/rat.go
src/math/big/rat_test.go
src/math/big/ratconv.go [new file with mode: 0644]
src/math/big/ratconv_test.go [new file with mode: 0644]

index bc4029a721f0a701968363e161d8f37c3f6d8b8c..b73377ea3f8ababb2c7aa33b2564e4e988f023ce 100644 (file)
@@ -10,10 +10,7 @@ import (
        "encoding/binary"
        "errors"
        "fmt"
-       "io"
        "math"
-       "strconv"
-       "strings"
 )
 
 // A Rat represents a quotient a/b of arbitrary precision.
@@ -514,229 +511,6 @@ func (z *Rat) Quo(x, y *Rat) *Rat {
        return z.norm()
 }
 
-func ratTok(ch rune) bool {
-       return strings.IndexRune("+-/0123456789.eE", ch) >= 0
-}
-
-// Scan is a support routine for fmt.Scanner. It accepts the formats
-// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent.
-func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
-       tok, err := s.Token(true, ratTok)
-       if err != nil {
-               return err
-       }
-       if strings.IndexRune("efgEFGv", ch) < 0 {
-               return errors.New("Rat.Scan: invalid verb")
-       }
-       if _, ok := z.SetString(string(tok)); !ok {
-               return errors.New("Rat.Scan: invalid syntax")
-       }
-       return nil
-}
-
-// SetString sets z to the value of s and returns z and a boolean indicating
-// success. s can be given as a fraction "a/b" or as a floating-point number
-// optionally followed by an exponent. If the operation failed, the value of
-// z is undefined but the returned value is nil.
-func (z *Rat) SetString(s string) (*Rat, bool) {
-       if len(s) == 0 {
-               return nil, false
-       }
-       // len(s) > 0
-
-       // parse fraction a/b, if any
-       if sep := strings.Index(s, "/"); sep >= 0 {
-               if _, ok := z.a.SetString(s[:sep], 0); !ok {
-                       return nil, false
-               }
-               s = s[sep+1:]
-               var err error
-               if z.b.abs, _, _, err = z.b.abs.scan(strings.NewReader(s), 0, false); err != nil {
-                       return nil, false
-               }
-               if len(z.b.abs) == 0 {
-                       return nil, false
-               }
-               return z.norm(), true
-       }
-
-       // parse floating-point number
-       r := strings.NewReader(s)
-
-       // sign
-       neg, err := scanSign(r)
-       if err != nil {
-               return nil, false
-       }
-
-       // mantissa
-       var ecorr int
-       z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true)
-       if err != nil {
-               return nil, false
-       }
-
-       // exponent
-       var exp int64
-       var ebase int
-       exp, ebase, err = scanExponent(r)
-       if ebase == 2 || err != nil {
-               return nil, false
-       }
-
-       // there should be no unread characters left
-       if _, err = r.ReadByte(); err != io.EOF {
-               return nil, false
-       }
-
-       // correct exponent
-       if ecorr < 0 {
-               exp += int64(ecorr)
-       }
-
-       // compute exponent power
-       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.norm()
-       } else {
-               z.a.abs = z.a.abs.mul(z.a.abs, powTen)
-               z.b.abs = z.b.abs[:0]
-       }
-
-       z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign
-
-       return z, true
-}
-
-func scanExponent(r io.ByteScanner) (exp int64, base int, err error) {
-       base = 10
-
-       var ch byte
-       if ch, err = r.ReadByte(); 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.UnreadByte()
-               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.ReadByte(); err != nil {
-                       if err != io.EOF || i == 0 {
-                               return
-                       }
-                       err = nil
-                       break // i > 0
-               }
-               if ch < '0' || '9' < ch {
-                       if i == 0 {
-                               r.UnreadByte()
-                               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"
-       if len(x.b.abs) != 0 {
-               s = "/" + x.b.abs.decimalString()
-       }
-       return x.a.String() + s
-}
-
-// RatString returns a string representation of x in the form "a/b" if b != 1,
-// and in the form "a" if b == 1.
-func (x *Rat) RatString() string {
-       if x.IsInt() {
-               return x.a.String()
-       }
-       return x.String()
-}
-
-// FloatString returns a string representation of x in decimal form with prec
-// digits of precision after the decimal point and the last digit rounded.
-func (x *Rat) FloatString(prec int) string {
-       if x.IsInt() {
-               s := x.a.String()
-               if prec > 0 {
-                       s += "." + strings.Repeat("0", prec)
-               }
-               return s
-       }
-       // x.b.abs != 0
-
-       q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)
-
-       p := natOne
-       if prec > 0 {
-               p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
-       }
-
-       r = r.mul(r, p)
-       r, r2 := r.div(nat(nil), r, x.b.abs)
-
-       // see if we need to round up
-       r2 = r2.add(r2, r2)
-       if x.b.abs.cmp(r2) <= 0 {
-               r = r.add(r, natOne)
-               if r.cmp(p) >= 0 {
-                       q = nat(nil).add(q, natOne)
-                       r = nat(nil).sub(r, p)
-               }
-       }
-
-       s := q.decimalString()
-       if x.a.neg {
-               s = "-" + s
-       }
-
-       if prec > 0 {
-               rs := r.decimalString()
-               leadingZeros := prec - len(rs)
-               s += "." + strings.Repeat("0", leadingZeros) + rs
-       }
-
-       return s
-}
-
 // Gob codec version. Permits backward-compatible changes to the encoding.
 const ratGobVersion byte = 1
 
index 37f672ee3d2b32dc383f7e4ca49eba9e5c886f66..012d0c47ec4fb857553e9bb6de246f102dfd8348 100644 (file)
@@ -9,10 +9,7 @@ import (
        "encoding/gob"
        "encoding/json"
        "encoding/xml"
-       "fmt"
        "math"
-       "strconv"
-       "strings"
        "testing"
 )
 
@@ -56,128 +53,6 @@ func TestZeroRat(t *testing.T) {
        z.Quo(&x, &y)
 }
 
-type StringTest struct {
-       in, out string
-       ok      bool
-}
-
-var setStringTests = []StringTest{
-       {"0", "0", true},
-       {"-0", "0", true},
-       {"1", "1", true},
-       {"-1", "-1", true},
-       {"1.", "1", true},
-       {"1e0", "1", true},
-       {"1.e1", "10", true},
-       {in: "1e"},
-       {in: "1.e"},
-       {in: "1e+14e-5"},
-       {in: "1e4.5"},
-       {in: "r"},
-       {in: "a/b"},
-       {in: "a.b"},
-       {"-0.1", "-1/10", true},
-       {"-.1", "-1/10", true},
-       {"2/4", "1/2", true},
-       {".25", "1/4", true},
-       {"-1/5", "-1/5", true},
-       {"8129567.7690E14", "812956776900000000000", true},
-       {"78189e+4", "781890000", true},
-       {"553019.8935e+8", "55301989350000", true},
-       {"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true},
-       {"9877861857500000E-7", "3951144743/4", true},
-       {"2169378.417e-3", "2169378417/1000000", true},
-       {"884243222337379604041632732738665534", "884243222337379604041632732738665534", true},
-       {"53/70893980658822810696", "53/70893980658822810696", true},
-       {"106/141787961317645621392", "53/70893980658822810696", true},
-       {"204211327800791583.81095", "4084226556015831676219/20000", true},
-       {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) {
-       var tests []StringTest
-       tests = append(tests, setStringTests...)
-       tests = append(tests, setStringTests2...)
-
-       for i, test := range tests {
-               x, ok := new(Rat).SetString(test.in)
-
-               if ok {
-                       if !test.ok {
-                               t.Errorf("#%d SetString(%q) expected failure", i, test.in)
-                       } else if x.RatString() != test.out {
-                               t.Errorf("#%d SetString(%q) got %s want %s", i, test.in, x.RatString(), test.out)
-                       }
-               } else if x != nil {
-                       t.Errorf("#%d SetString(%q) got %p want nil", i, test.in, x)
-               }
-       }
-}
-
-func TestRatScan(t *testing.T) {
-       var buf bytes.Buffer
-       for i, test := range setStringTests {
-               x := new(Rat)
-               buf.Reset()
-               buf.WriteString(test.in)
-
-               _, err := fmt.Fscanf(&buf, "%v", x)
-               if err == nil != test.ok {
-                       if test.ok {
-                               t.Errorf("#%d (%s) error: %s", i, test.in, err)
-                       } else {
-                               t.Errorf("#%d (%s) expected error", i, test.in)
-                       }
-                       continue
-               }
-               if err == nil && x.RatString() != test.out {
-                       t.Errorf("#%d got %s want %s", i, x.RatString(), test.out)
-               }
-       }
-}
-
-var floatStringTests = []struct {
-       in   string
-       prec int
-       out  string
-}{
-       {"0", 0, "0"},
-       {"0", 4, "0.0000"},
-       {"1", 0, "1"},
-       {"1", 2, "1.00"},
-       {"-1", 0, "-1"},
-       {".25", 2, "0.25"},
-       {".25", 1, "0.3"},
-       {".25", 3, "0.250"},
-       {"-1/3", 3, "-0.333"},
-       {"-2/3", 4, "-0.6667"},
-       {"0.96", 1, "1.0"},
-       {"0.999", 2, "1.00"},
-       {"0.9", 0, "1"},
-       {".25", -1, "0"},
-       {".55", -1, "1"},
-}
-
-func TestFloatString(t *testing.T) {
-       for i, test := range floatStringTests {
-               x, _ := new(Rat).SetString(test.in)
-
-               if x.FloatString(test.prec) != test.out {
-                       t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out)
-               }
-       }
-}
-
 func TestRatSign(t *testing.T) {
        zero := NewRat(0, 1)
        for _, a := range setStringTests {
@@ -608,321 +483,6 @@ func TestIssue3521(t *testing.T) {
        }
 }
 
-// Test inputs to Rat.SetString.  The prefix "long:" causes the test
-// to be skipped in --test.short mode.  (The threshold is about 500us.)
-var float64inputs = []string{
-       // Constants plundered from strconv/testfp.txt.
-
-       // Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
-       "5e+125",
-       "69e+267",
-       "999e-026",
-       "7861e-034",
-       "75569e-254",
-       "928609e-261",
-       "9210917e+080",
-       "84863171e+114",
-       "653777767e+273",
-       "5232604057e-298",
-       "27235667517e-109",
-       "653532977297e-123",
-       "3142213164987e-294",
-       "46202199371337e-072",
-       "231010996856685e-073",
-       "9324754620109615e+212",
-       "78459735791271921e+049",
-       "272104041512242479e+200",
-       "6802601037806061975e+198",
-       "20505426358836677347e-221",
-       "836168422905420598437e-234",
-       "4891559871276714924261e+222",
-
-       // Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP
-       "9e-265",
-       "85e-037",
-       "623e+100",
-       "3571e+263",
-       "81661e+153",
-       "920657e-023",
-       "4603285e-024",
-       "87575437e-309",
-       "245540327e+122",
-       "6138508175e+120",
-       "83356057653e+193",
-       "619534293513e+124",
-       "2335141086879e+218",
-       "36167929443327e-159",
-       "609610927149051e-255",
-       "3743626360493413e-165",
-       "94080055902682397e-242",
-       "899810892172646163e+283",
-       "7120190517612959703e+120",
-       "25188282901709339043e-252",
-       "308984926168550152811e-052",
-       "6372891218502368041059e+064",
-
-       // Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP
-       "5e-20",
-       "67e+14",
-       "985e+15",
-       "7693e-42",
-       "55895e-16",
-       "996622e-44",
-       "7038531e-32",
-       "60419369e-46",
-       "702990899e-20",
-       "6930161142e-48",
-       "25933168707e+13",
-       "596428896559e+20",
-
-       // Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP
-       "3e-23",
-       "57e+18",
-       "789e-35",
-       "2539e-18",
-       "76173e+28",
-       "887745e-11",
-       "5382571e-37",
-       "82381273e-35",
-       "750486563e-38",
-       "3752432815e-39",
-       "75224575729e-45",
-       "459926601011e+15",
-
-       // Constants plundered from strconv/atof_test.go.
-
-       "0",
-       "1",
-       "+1",
-       "1e23",
-       "1E23",
-       "100000000000000000000000",
-       "1e-100",
-       "123456700",
-       "99999999999999974834176",
-       "100000000000000000000001",
-       "100000000000000008388608",
-       "100000000000000016777215",
-       "100000000000000016777216",
-       "-1",
-       "-0.1",
-       "-0", // NB: exception made for this input
-       "1e-20",
-       "625e-3",
-
-       // largest float64
-       "1.7976931348623157e308",
-       "-1.7976931348623157e308",
-       // next float64 - too large
-       "1.7976931348623159e308",
-       "-1.7976931348623159e308",
-       // the border is ...158079
-       // borderline - okay
-       "1.7976931348623158e308",
-       "-1.7976931348623158e308",
-       // borderline - too large
-       "1.797693134862315808e308",
-       "-1.797693134862315808e308",
-
-       // a little too large
-       "1e308",
-       "2e308",
-       "1e309",
-
-       // way too large
-       "1e310",
-       "-1e310",
-       "1e400",
-       "-1e400",
-       "long:1e400000",
-       "long:-1e400000",
-
-       // denormalized
-       "1e-305",
-       "1e-306",
-       "1e-307",
-       "1e-308",
-       "1e-309",
-       "1e-310",
-       "1e-322",
-       // smallest denormal
-       "5e-324",
-       "4e-324",
-       "3e-324",
-       // too small
-       "2e-324",
-       // way too small
-       "1e-350",
-       "long:1e-400000",
-       // way too small, negative
-       "-1e-350",
-       "long:-1e-400000",
-
-       // try to overflow exponent
-       // [Disabled: too slow and memory-hungry with rationals.]
-       // "1e-4294967296",
-       // "1e+4294967296",
-       // "1e-18446744073709551616",
-       // "1e+18446744073709551616",
-
-       // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
-       "2.2250738585072012e-308",
-       // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
-       "2.2250738585072011e-308",
-
-       // A very large number (initially wrongly parsed by the fast algorithm).
-       "4.630813248087435e+307",
-
-       // A different kind of very large number.
-       "22.222222222222222",
-       "long:2." + strings.Repeat("2", 4000) + "e+1",
-
-       // Exactly halfway between 1 and math.Nextafter(1, 2).
-       // Round to even (down).
-       "1.00000000000000011102230246251565404236316680908203125",
-       // Slightly lower; still round down.
-       "1.00000000000000011102230246251565404236316680908203124",
-       // Slightly higher; round up.
-       "1.00000000000000011102230246251565404236316680908203126",
-       // Slightly higher, but you have to read all the way to the end.
-       "long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1",
-
-       // Smallest denormal, 2^(-1022-52)
-       "4.940656458412465441765687928682213723651e-324",
-       // Half of smallest denormal, 2^(-1022-53)
-       "2.470328229206232720882843964341106861825e-324",
-       // A little more than the exact half of smallest denormal
-       // 2^-1075 + 2^-1100.  (Rounds to 1p-1074.)
-       "2.470328302827751011111470718709768633275e-324",
-       // The exact halfway between smallest normal and largest denormal:
-       // 2^-1022 - 2^-1075.  (Rounds to 2^-1022.)
-       "2.225073858507201136057409796709131975935e-308",
-
-       "1152921504606846975",  //   1<<60 - 1
-       "-1152921504606846975", // -(1<<60 - 1)
-       "1152921504606846977",  //   1<<60 + 1
-       "-1152921504606846977", // -(1<<60 + 1)
-
-       "1/3",
-}
-
-// isFinite reports whether f represents a finite rational value.
-// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0).
-func isFinite(f float64) bool {
-       return math.Abs(f) <= math.MaxFloat64
-}
-
-func TestFloat32SpecialCases(t *testing.T) {
-       for _, input := range float64inputs {
-               if strings.HasPrefix(input, "long:") {
-                       if testing.Short() {
-                               continue
-                       }
-                       input = input[len("long:"):]
-               }
-
-               r, ok := new(Rat).SetString(input)
-               if !ok {
-                       t.Errorf("Rat.SetString(%q) failed", input)
-                       continue
-               }
-               f, exact := r.Float32()
-
-               // 1. Check string -> Rat -> float32 conversions are
-               // consistent with strconv.ParseFloat.
-               // Skip this check if the input uses "a/b" rational syntax.
-               if !strings.Contains(input, "/") {
-                       e64, _ := strconv.ParseFloat(input, 32)
-                       e := float32(e64)
-
-                       // Careful: negative Rats too small for
-                       // float64 become -0, but Rat obviously cannot
-                       // preserve the sign from SetString("-0").
-                       switch {
-                       case math.Float32bits(e) == math.Float32bits(f):
-                               // Ok: bitwise equal.
-                       case f == 0 && r.Num().BitLen() == 0:
-                               // Ok: Rat(0) is equivalent to both +/- float64(0).
-                       default:
-                               t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
-                       }
-               }
-
-               if !isFinite(float64(f)) {
-                       continue
-               }
-
-               // 2. Check f is best approximation to r.
-               if !checkIsBestApprox32(t, f, r) {
-                       // Append context information.
-                       t.Errorf("(input was %q)", input)
-               }
-
-               // 3. Check f->R->f roundtrip is non-lossy.
-               checkNonLossyRoundtrip32(t, f)
-
-               // 4. Check exactness using slow algorithm.
-               if wasExact := new(Rat).SetFloat64(float64(f)).Cmp(r) == 0; wasExact != exact {
-                       t.Errorf("Rat.SetString(%q).Float32().exact = %t, want %t", input, exact, wasExact)
-               }
-       }
-}
-
-func TestFloat64SpecialCases(t *testing.T) {
-       for _, input := range float64inputs {
-               if strings.HasPrefix(input, "long:") {
-                       if testing.Short() {
-                               continue
-                       }
-                       input = input[len("long:"):]
-               }
-
-               r, ok := new(Rat).SetString(input)
-               if !ok {
-                       t.Errorf("Rat.SetString(%q) failed", input)
-                       continue
-               }
-               f, exact := r.Float64()
-
-               // 1. Check string -> Rat -> float64 conversions are
-               // consistent with strconv.ParseFloat.
-               // Skip this check if the input uses "a/b" rational syntax.
-               if !strings.Contains(input, "/") {
-                       e, _ := strconv.ParseFloat(input, 64)
-
-                       // Careful: negative Rats too small for
-                       // float64 become -0, but Rat obviously cannot
-                       // preserve the sign from SetString("-0").
-                       switch {
-                       case math.Float64bits(e) == math.Float64bits(f):
-                               // Ok: bitwise equal.
-                       case f == 0 && r.Num().BitLen() == 0:
-                               // Ok: Rat(0) is equivalent to both +/- float64(0).
-                       default:
-                               t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
-                       }
-               }
-
-               if !isFinite(f) {
-                       continue
-               }
-
-               // 2. Check f is best approximation to r.
-               if !checkIsBestApprox64(t, f, r) {
-                       // Append context information.
-                       t.Errorf("(input was %q)", input)
-               }
-
-               // 3. Check f->R->f roundtrip is non-lossy.
-               checkNonLossyRoundtrip64(t, f)
-
-               // 4. Check exactness using slow algorithm.
-               if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact {
-                       t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact)
-               }
-       }
-}
-
 func TestFloat32Distribution(t *testing.T) {
        // Generate a distribution of (sign, mantissa, exp) values
        // broader than the float32 range, and check Rat.Float32()
diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go
new file mode 100644 (file)
index 0000000..da4915e
--- /dev/null
@@ -0,0 +1,238 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements rat-to-string conversion functions.
+
+package big
+
+import (
+       "errors"
+       "fmt"
+       "io"
+       "strconv"
+       "strings"
+)
+
+func ratTok(ch rune) bool {
+       return strings.IndexRune("+-/0123456789.eE", ch) >= 0
+}
+
+// Scan is a support routine for fmt.Scanner. It accepts the formats
+// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent.
+func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
+       tok, err := s.Token(true, ratTok)
+       if err != nil {
+               return err
+       }
+       if strings.IndexRune("efgEFGv", ch) < 0 {
+               return errors.New("Rat.Scan: invalid verb")
+       }
+       if _, ok := z.SetString(string(tok)); !ok {
+               return errors.New("Rat.Scan: invalid syntax")
+       }
+       return nil
+}
+
+// SetString sets z to the value of s and returns z and a boolean indicating
+// success. s can be given as a fraction "a/b" or as a floating-point number
+// optionally followed by an exponent. If the operation failed, the value of
+// z is undefined but the returned value is nil.
+func (z *Rat) SetString(s string) (*Rat, bool) {
+       if len(s) == 0 {
+               return nil, false
+       }
+       // len(s) > 0
+
+       // parse fraction a/b, if any
+       if sep := strings.Index(s, "/"); sep >= 0 {
+               if _, ok := z.a.SetString(s[:sep], 0); !ok {
+                       return nil, false
+               }
+               s = s[sep+1:]
+               var err error
+               if z.b.abs, _, _, err = z.b.abs.scan(strings.NewReader(s), 0, false); err != nil {
+                       return nil, false
+               }
+               if len(z.b.abs) == 0 {
+                       return nil, false
+               }
+               return z.norm(), true
+       }
+
+       // parse floating-point number
+       r := strings.NewReader(s)
+
+       // sign
+       neg, err := scanSign(r)
+       if err != nil {
+               return nil, false
+       }
+
+       // mantissa
+       var ecorr int
+       z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true)
+       if err != nil {
+               return nil, false
+       }
+
+       // exponent
+       var exp int64
+       var ebase int
+       exp, ebase, err = scanExponent(r)
+       if ebase == 2 || err != nil {
+               return nil, false
+       }
+
+       // there should be no unread characters left
+       if _, err = r.ReadByte(); err != io.EOF {
+               return nil, false
+       }
+
+       // correct exponent
+       if ecorr < 0 {
+               exp += int64(ecorr)
+       }
+
+       // compute exponent power
+       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.norm()
+       } else {
+               z.a.abs = z.a.abs.mul(z.a.abs, powTen)
+               z.b.abs = z.b.abs[:0]
+       }
+
+       z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign
+
+       return z, true
+}
+
+func scanExponent(r io.ByteScanner) (exp int64, base int, err error) {
+       base = 10
+
+       var ch byte
+       if ch, err = r.ReadByte(); 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.UnreadByte()
+               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.ReadByte(); err != nil {
+                       if err != io.EOF || i == 0 {
+                               return
+                       }
+                       err = nil
+                       break // i > 0
+               }
+               if ch < '0' || '9' < ch {
+                       if i == 0 {
+                               r.UnreadByte()
+                               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"
+       if len(x.b.abs) != 0 {
+               s = "/" + x.b.abs.decimalString()
+       }
+       return x.a.String() + s
+}
+
+// RatString returns a string representation of x in the form "a/b" if b != 1,
+// and in the form "a" if b == 1.
+func (x *Rat) RatString() string {
+       if x.IsInt() {
+               return x.a.String()
+       }
+       return x.String()
+}
+
+// FloatString returns a string representation of x in decimal form with prec
+// digits of precision after the decimal point and the last digit rounded.
+func (x *Rat) FloatString(prec int) string {
+       if x.IsInt() {
+               s := x.a.String()
+               if prec > 0 {
+                       s += "." + strings.Repeat("0", prec)
+               }
+               return s
+       }
+       // x.b.abs != 0
+
+       q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)
+
+       p := natOne
+       if prec > 0 {
+               p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
+       }
+
+       r = r.mul(r, p)
+       r, r2 := r.div(nat(nil), r, x.b.abs)
+
+       // see if we need to round up
+       r2 = r2.add(r2, r2)
+       if x.b.abs.cmp(r2) <= 0 {
+               r = r.add(r, natOne)
+               if r.cmp(p) >= 0 {
+                       q = nat(nil).add(q, natOne)
+                       r = nat(nil).sub(r, p)
+               }
+       }
+
+       s := q.decimalString()
+       if x.a.neg {
+               s = "-" + s
+       }
+
+       if prec > 0 {
+               rs := r.decimalString()
+               leadingZeros := prec - len(rs)
+               s += "." + strings.Repeat("0", leadingZeros) + rs
+       }
+
+       return s
+}
diff --git a/src/math/big/ratconv_test.go b/src/math/big/ratconv_test.go
new file mode 100644 (file)
index 0000000..16b3a19
--- /dev/null
@@ -0,0 +1,451 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package big
+
+import (
+       "bytes"
+       "fmt"
+       "math"
+       "strconv"
+       "strings"
+       "testing"
+)
+
+type StringTest struct {
+       in, out string
+       ok      bool
+}
+
+var setStringTests = []StringTest{
+       {"0", "0", true},
+       {"-0", "0", true},
+       {"1", "1", true},
+       {"-1", "-1", true},
+       {"1.", "1", true},
+       {"1e0", "1", true},
+       {"1.e1", "10", true},
+       {in: "1e"},
+       {in: "1.e"},
+       {in: "1e+14e-5"},
+       {in: "1e4.5"},
+       {in: "r"},
+       {in: "a/b"},
+       {in: "a.b"},
+       {"-0.1", "-1/10", true},
+       {"-.1", "-1/10", true},
+       {"2/4", "1/2", true},
+       {".25", "1/4", true},
+       {"-1/5", "-1/5", true},
+       {"8129567.7690E14", "812956776900000000000", true},
+       {"78189e+4", "781890000", true},
+       {"553019.8935e+8", "55301989350000", true},
+       {"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true},
+       {"9877861857500000E-7", "3951144743/4", true},
+       {"2169378.417e-3", "2169378417/1000000", true},
+       {"884243222337379604041632732738665534", "884243222337379604041632732738665534", true},
+       {"53/70893980658822810696", "53/70893980658822810696", true},
+       {"106/141787961317645621392", "53/70893980658822810696", true},
+       {"204211327800791583.81095", "4084226556015831676219/20000", true},
+       {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) {
+       var tests []StringTest
+       tests = append(tests, setStringTests...)
+       tests = append(tests, setStringTests2...)
+
+       for i, test := range tests {
+               x, ok := new(Rat).SetString(test.in)
+
+               if ok {
+                       if !test.ok {
+                               t.Errorf("#%d SetString(%q) expected failure", i, test.in)
+                       } else if x.RatString() != test.out {
+                               t.Errorf("#%d SetString(%q) got %s want %s", i, test.in, x.RatString(), test.out)
+                       }
+               } else if x != nil {
+                       t.Errorf("#%d SetString(%q) got %p want nil", i, test.in, x)
+               }
+       }
+}
+
+func TestRatScan(t *testing.T) {
+       var buf bytes.Buffer
+       for i, test := range setStringTests {
+               x := new(Rat)
+               buf.Reset()
+               buf.WriteString(test.in)
+
+               _, err := fmt.Fscanf(&buf, "%v", x)
+               if err == nil != test.ok {
+                       if test.ok {
+                               t.Errorf("#%d (%s) error: %s", i, test.in, err)
+                       } else {
+                               t.Errorf("#%d (%s) expected error", i, test.in)
+                       }
+                       continue
+               }
+               if err == nil && x.RatString() != test.out {
+                       t.Errorf("#%d got %s want %s", i, x.RatString(), test.out)
+               }
+       }
+}
+
+var floatStringTests = []struct {
+       in   string
+       prec int
+       out  string
+}{
+       {"0", 0, "0"},
+       {"0", 4, "0.0000"},
+       {"1", 0, "1"},
+       {"1", 2, "1.00"},
+       {"-1", 0, "-1"},
+       {".25", 2, "0.25"},
+       {".25", 1, "0.3"},
+       {".25", 3, "0.250"},
+       {"-1/3", 3, "-0.333"},
+       {"-2/3", 4, "-0.6667"},
+       {"0.96", 1, "1.0"},
+       {"0.999", 2, "1.00"},
+       {"0.9", 0, "1"},
+       {".25", -1, "0"},
+       {".55", -1, "1"},
+}
+
+func TestFloatString(t *testing.T) {
+       for i, test := range floatStringTests {
+               x, _ := new(Rat).SetString(test.in)
+
+               if x.FloatString(test.prec) != test.out {
+                       t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out)
+               }
+       }
+}
+
+// Test inputs to Rat.SetString.  The prefix "long:" causes the test
+// to be skipped in --test.short mode.  (The threshold is about 500us.)
+var float64inputs = []string{
+       // Constants plundered from strconv/testfp.txt.
+
+       // Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
+       "5e+125",
+       "69e+267",
+       "999e-026",
+       "7861e-034",
+       "75569e-254",
+       "928609e-261",
+       "9210917e+080",
+       "84863171e+114",
+       "653777767e+273",
+       "5232604057e-298",
+       "27235667517e-109",
+       "653532977297e-123",
+       "3142213164987e-294",
+       "46202199371337e-072",
+       "231010996856685e-073",
+       "9324754620109615e+212",
+       "78459735791271921e+049",
+       "272104041512242479e+200",
+       "6802601037806061975e+198",
+       "20505426358836677347e-221",
+       "836168422905420598437e-234",
+       "4891559871276714924261e+222",
+
+       // Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP
+       "9e-265",
+       "85e-037",
+       "623e+100",
+       "3571e+263",
+       "81661e+153",
+       "920657e-023",
+       "4603285e-024",
+       "87575437e-309",
+       "245540327e+122",
+       "6138508175e+120",
+       "83356057653e+193",
+       "619534293513e+124",
+       "2335141086879e+218",
+       "36167929443327e-159",
+       "609610927149051e-255",
+       "3743626360493413e-165",
+       "94080055902682397e-242",
+       "899810892172646163e+283",
+       "7120190517612959703e+120",
+       "25188282901709339043e-252",
+       "308984926168550152811e-052",
+       "6372891218502368041059e+064",
+
+       // Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP
+       "5e-20",
+       "67e+14",
+       "985e+15",
+       "7693e-42",
+       "55895e-16",
+       "996622e-44",
+       "7038531e-32",
+       "60419369e-46",
+       "702990899e-20",
+       "6930161142e-48",
+       "25933168707e+13",
+       "596428896559e+20",
+
+       // Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP
+       "3e-23",
+       "57e+18",
+       "789e-35",
+       "2539e-18",
+       "76173e+28",
+       "887745e-11",
+       "5382571e-37",
+       "82381273e-35",
+       "750486563e-38",
+       "3752432815e-39",
+       "75224575729e-45",
+       "459926601011e+15",
+
+       // Constants plundered from strconv/atof_test.go.
+
+       "0",
+       "1",
+       "+1",
+       "1e23",
+       "1E23",
+       "100000000000000000000000",
+       "1e-100",
+       "123456700",
+       "99999999999999974834176",
+       "100000000000000000000001",
+       "100000000000000008388608",
+       "100000000000000016777215",
+       "100000000000000016777216",
+       "-1",
+       "-0.1",
+       "-0", // NB: exception made for this input
+       "1e-20",
+       "625e-3",
+
+       // largest float64
+       "1.7976931348623157e308",
+       "-1.7976931348623157e308",
+       // next float64 - too large
+       "1.7976931348623159e308",
+       "-1.7976931348623159e308",
+       // the border is ...158079
+       // borderline - okay
+       "1.7976931348623158e308",
+       "-1.7976931348623158e308",
+       // borderline - too large
+       "1.797693134862315808e308",
+       "-1.797693134862315808e308",
+
+       // a little too large
+       "1e308",
+       "2e308",
+       "1e309",
+
+       // way too large
+       "1e310",
+       "-1e310",
+       "1e400",
+       "-1e400",
+       "long:1e400000",
+       "long:-1e400000",
+
+       // denormalized
+       "1e-305",
+       "1e-306",
+       "1e-307",
+       "1e-308",
+       "1e-309",
+       "1e-310",
+       "1e-322",
+       // smallest denormal
+       "5e-324",
+       "4e-324",
+       "3e-324",
+       // too small
+       "2e-324",
+       // way too small
+       "1e-350",
+       "long:1e-400000",
+       // way too small, negative
+       "-1e-350",
+       "long:-1e-400000",
+
+       // try to overflow exponent
+       // [Disabled: too slow and memory-hungry with rationals.]
+       // "1e-4294967296",
+       // "1e+4294967296",
+       // "1e-18446744073709551616",
+       // "1e+18446744073709551616",
+
+       // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
+       "2.2250738585072012e-308",
+       // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
+       "2.2250738585072011e-308",
+
+       // A very large number (initially wrongly parsed by the fast algorithm).
+       "4.630813248087435e+307",
+
+       // A different kind of very large number.
+       "22.222222222222222",
+       "long:2." + strings.Repeat("2", 4000) + "e+1",
+
+       // Exactly halfway between 1 and math.Nextafter(1, 2).
+       // Round to even (down).
+       "1.00000000000000011102230246251565404236316680908203125",
+       // Slightly lower; still round down.
+       "1.00000000000000011102230246251565404236316680908203124",
+       // Slightly higher; round up.
+       "1.00000000000000011102230246251565404236316680908203126",
+       // Slightly higher, but you have to read all the way to the end.
+       "long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1",
+
+       // Smallest denormal, 2^(-1022-52)
+       "4.940656458412465441765687928682213723651e-324",
+       // Half of smallest denormal, 2^(-1022-53)
+       "2.470328229206232720882843964341106861825e-324",
+       // A little more than the exact half of smallest denormal
+       // 2^-1075 + 2^-1100.  (Rounds to 1p-1074.)
+       "2.470328302827751011111470718709768633275e-324",
+       // The exact halfway between smallest normal and largest denormal:
+       // 2^-1022 - 2^-1075.  (Rounds to 2^-1022.)
+       "2.225073858507201136057409796709131975935e-308",
+
+       "1152921504606846975",  //   1<<60 - 1
+       "-1152921504606846975", // -(1<<60 - 1)
+       "1152921504606846977",  //   1<<60 + 1
+       "-1152921504606846977", // -(1<<60 + 1)
+
+       "1/3",
+}
+
+// isFinite reports whether f represents a finite rational value.
+// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0).
+func isFinite(f float64) bool {
+       return math.Abs(f) <= math.MaxFloat64
+}
+
+func TestFloat32SpecialCases(t *testing.T) {
+       for _, input := range float64inputs {
+               if strings.HasPrefix(input, "long:") {
+                       if testing.Short() {
+                               continue
+                       }
+                       input = input[len("long:"):]
+               }
+
+               r, ok := new(Rat).SetString(input)
+               if !ok {
+                       t.Errorf("Rat.SetString(%q) failed", input)
+                       continue
+               }
+               f, exact := r.Float32()
+
+               // 1. Check string -> Rat -> float32 conversions are
+               // consistent with strconv.ParseFloat.
+               // Skip this check if the input uses "a/b" rational syntax.
+               if !strings.Contains(input, "/") {
+                       e64, _ := strconv.ParseFloat(input, 32)
+                       e := float32(e64)
+
+                       // Careful: negative Rats too small for
+                       // float64 become -0, but Rat obviously cannot
+                       // preserve the sign from SetString("-0").
+                       switch {
+                       case math.Float32bits(e) == math.Float32bits(f):
+                               // Ok: bitwise equal.
+                       case f == 0 && r.Num().BitLen() == 0:
+                               // Ok: Rat(0) is equivalent to both +/- float64(0).
+                       default:
+                               t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
+                       }
+               }
+
+               if !isFinite(float64(f)) {
+                       continue
+               }
+
+               // 2. Check f is best approximation to r.
+               if !checkIsBestApprox32(t, f, r) {
+                       // Append context information.
+                       t.Errorf("(input was %q)", input)
+               }
+
+               // 3. Check f->R->f roundtrip is non-lossy.
+               checkNonLossyRoundtrip32(t, f)
+
+               // 4. Check exactness using slow algorithm.
+               if wasExact := new(Rat).SetFloat64(float64(f)).Cmp(r) == 0; wasExact != exact {
+                       t.Errorf("Rat.SetString(%q).Float32().exact = %t, want %t", input, exact, wasExact)
+               }
+       }
+}
+
+func TestFloat64SpecialCases(t *testing.T) {
+       for _, input := range float64inputs {
+               if strings.HasPrefix(input, "long:") {
+                       if testing.Short() {
+                               continue
+                       }
+                       input = input[len("long:"):]
+               }
+
+               r, ok := new(Rat).SetString(input)
+               if !ok {
+                       t.Errorf("Rat.SetString(%q) failed", input)
+                       continue
+               }
+               f, exact := r.Float64()
+
+               // 1. Check string -> Rat -> float64 conversions are
+               // consistent with strconv.ParseFloat.
+               // Skip this check if the input uses "a/b" rational syntax.
+               if !strings.Contains(input, "/") {
+                       e, _ := strconv.ParseFloat(input, 64)
+
+                       // Careful: negative Rats too small for
+                       // float64 become -0, but Rat obviously cannot
+                       // preserve the sign from SetString("-0").
+                       switch {
+                       case math.Float64bits(e) == math.Float64bits(f):
+                               // Ok: bitwise equal.
+                       case f == 0 && r.Num().BitLen() == 0:
+                               // Ok: Rat(0) is equivalent to both +/- float64(0).
+                       default:
+                               t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
+                       }
+               }
+
+               if !isFinite(f) {
+                       continue
+               }
+
+               // 2. Check f is best approximation to r.
+               if !checkIsBestApprox64(t, f, r) {
+                       // Append context information.
+                       t.Errorf("(input was %q)", input)
+               }
+
+               // 3. Check f->R->f roundtrip is non-lossy.
+               checkNonLossyRoundtrip64(t, f)
+
+               // 4. Check exactness using slow algorithm.
+               if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact {
+                       t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact)
+               }
+       }
+}