| // 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.ContainsRune("+-/0123456789.eE", ch) | |
| } | |
| var ratZero Rat | |
| var _ fmt.Scanner = &ratZero // *Rat must implement fmt.Scanner | |
| // 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.ContainsRune("efgEFGv", ch) { | |
| 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. The entire string (not just a prefix) | |
| // must be valid for success. 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 | |
| } | |
| r := strings.NewReader(s[sep+1:]) | |
| var err error | |
| if z.b.abs, _, _, err = z.b.abs.scan(r, 0, false); err != nil { | |
| return nil, false | |
| } | |
| // entire string must have been consumed | |
| if _, err = r.ReadByte(); err != io.EOF { | |
| 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 | |
| exp, _, err = scanExponent(r, false) | |
| if err != nil { | |
| return nil, false | |
| } | |
| // there should be no unread characters left | |
| if _, err = r.ReadByte(); err != io.EOF { | |
| return nil, false | |
| } | |
| // special-case 0 (see also issue #16176) | |
| if len(z.a.abs) == 0 { | |
| return z, true | |
| } | |
| // len(z.a.abs) > 0 | |
| // 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 | |
| } | |
| // scanExponent scans the longest possible prefix of r representing a decimal | |
| // ('e', 'E') or binary ('p') exponent, if any. It returns the exponent, the | |
| // exponent base (10 or 2), or a read or syntax error, if any. | |
| // | |
| // exponent = ( "E" | "e" | "p" ) [ sign ] digits . | |
| // sign = "+" | "-" . | |
| // digits = digit { digit } . | |
| // digit = "0" ... "9" . | |
| // | |
| // A binary exponent is only permitted if binExpOk is set. | |
| func scanExponent(r io.ByteScanner, binExpOk bool) (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': | |
| if binExpOk { | |
| base = 2 | |
| break // ok | |
| } | |
| fallthrough // binary exponent not permitted | |
| 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, 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 { | |
| return string(x.marshal()) | |
| } | |
| // marshal implements String returning a slice of bytes | |
| func (x *Rat) marshal() []byte { | |
| var buf []byte | |
| buf = x.a.Append(buf, 10) | |
| buf = append(buf, '/') | |
| if len(x.b.abs) != 0 { | |
| buf = x.b.Append(buf, 10) | |
| } else { | |
| buf = append(buf, '1') | |
| } | |
| return buf | |
| } | |
| // 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. The last digit is rounded to | |
| // nearest, with halves rounded away from zero. | |
| func (x *Rat) FloatString(prec int) string { | |
| var buf []byte | |
| if x.IsInt() { | |
| buf = x.a.Append(buf, 10) | |
| if prec > 0 { | |
| buf = append(buf, '.') | |
| for i := prec; i > 0; i-- { | |
| buf = append(buf, '0') | |
| } | |
| } | |
| return string(buf) | |
| } | |
| // 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) | |
| } | |
| } | |
| if x.a.neg { | |
| buf = append(buf, '-') | |
| } | |
| buf = append(buf, q.utoa(10)...) // itoa ignores sign if q == 0 | |
| if prec > 0 { | |
| buf = append(buf, '.') | |
| rs := r.utoa(10) | |
| for i := prec - len(rs); i > 0; i-- { | |
| buf = append(buf, '0') | |
| } | |
| buf = append(buf, rs...) | |
| } | |
| return string(buf) | |
| } |