...
Run Format

Source file src/math/big/ratconv.go

Documentation: math/big

  // 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 {
  	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 string(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)
  }
  

View as plain text