...

Source file src/math/big/ratconv.go

Documentation: math/big

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file implements rat-to-string conversion functions.
     6  
     7  package big
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"strconv"
    14  	"strings"
    15  )
    16  
    17  func ratTok(ch rune) bool {
    18  	return strings.ContainsRune("+-/0123456789.eE", ch)
    19  }
    20  
    21  var ratZero Rat
    22  var _ fmt.Scanner = &ratZero // *Rat must implement fmt.Scanner
    23  
    24  // Scan is a support routine for fmt.Scanner. It accepts the formats
    25  // 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent.
    26  func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
    27  	tok, err := s.Token(true, ratTok)
    28  	if err != nil {
    29  		return err
    30  	}
    31  	if !strings.ContainsRune("efgEFGv", ch) {
    32  		return errors.New("Rat.Scan: invalid verb")
    33  	}
    34  	if _, ok := z.SetString(string(tok)); !ok {
    35  		return errors.New("Rat.Scan: invalid syntax")
    36  	}
    37  	return nil
    38  }
    39  
    40  // SetString sets z to the value of s and returns z and a boolean indicating
    41  // success. s can be given as a fraction "a/b" or as a decimal floating-point
    42  // number optionally followed by an exponent. The entire string (not just a prefix)
    43  // must be valid for success. If the operation failed, the value of z is
    44  // undefined but the returned value is nil.
    45  func (z *Rat) SetString(s string) (*Rat, bool) {
    46  	if len(s) == 0 {
    47  		return nil, false
    48  	}
    49  	// len(s) > 0
    50  
    51  	// parse fraction a/b, if any
    52  	if sep := strings.Index(s, "/"); sep >= 0 {
    53  		if _, ok := z.a.SetString(s[:sep], 0); !ok {
    54  			return nil, false
    55  		}
    56  		r := strings.NewReader(s[sep+1:])
    57  		var err error
    58  		if z.b.abs, _, _, err = z.b.abs.scan(r, 0, false); err != nil {
    59  			return nil, false
    60  		}
    61  		// entire string must have been consumed
    62  		if _, err = r.ReadByte(); err != io.EOF {
    63  			return nil, false
    64  		}
    65  		if len(z.b.abs) == 0 {
    66  			return nil, false
    67  		}
    68  		return z.norm(), true
    69  	}
    70  
    71  	// parse floating-point number
    72  	r := strings.NewReader(s)
    73  
    74  	// sign
    75  	neg, err := scanSign(r)
    76  	if err != nil {
    77  		return nil, false
    78  	}
    79  
    80  	// mantissa
    81  	// TODO(gri) allow other bases besides 10 for mantissa and exponent? (issue #29799)
    82  	var ecorr int
    83  	z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true)
    84  	if err != nil {
    85  		return nil, false
    86  	}
    87  
    88  	// exponent
    89  	var exp int64
    90  	exp, _, err = scanExponent(r, false)
    91  	if err != nil {
    92  		return nil, false
    93  	}
    94  
    95  	// there should be no unread characters left
    96  	if _, err = r.ReadByte(); err != io.EOF {
    97  		return nil, false
    98  	}
    99  
   100  	// special-case 0 (see also issue #16176)
   101  	if len(z.a.abs) == 0 {
   102  		return z, true
   103  	}
   104  	// len(z.a.abs) > 0
   105  
   106  	// correct exponent
   107  	if ecorr < 0 {
   108  		exp += int64(ecorr)
   109  	}
   110  
   111  	// compute exponent power
   112  	expabs := exp
   113  	if expabs < 0 {
   114  		expabs = -expabs
   115  	}
   116  	powTen := nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil)
   117  
   118  	// complete fraction
   119  	if exp < 0 {
   120  		z.b.abs = powTen
   121  		z.norm()
   122  	} else {
   123  		z.a.abs = z.a.abs.mul(z.a.abs, powTen)
   124  		z.b.abs = z.b.abs[:0]
   125  	}
   126  
   127  	z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign
   128  
   129  	return z, true
   130  }
   131  
   132  // scanExponent scans the longest possible prefix of r representing a decimal
   133  // ('e', 'E') or binary ('p') exponent, if any. It returns the exponent, the
   134  // exponent base (10 or 2), or a read or syntax error, if any.
   135  //
   136  //	exponent = ( "E" | "e" | "p" ) [ sign ] digits .
   137  //	sign     = "+" | "-" .
   138  //	digits   = digit { digit } .
   139  //	digit    = "0" ... "9" .
   140  //
   141  // A binary exponent is only permitted if binExpOk is set.
   142  func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err error) {
   143  	base = 10
   144  
   145  	var ch byte
   146  	if ch, err = r.ReadByte(); err != nil {
   147  		if err == io.EOF {
   148  			err = nil // no exponent; same as e0
   149  		}
   150  		return
   151  	}
   152  
   153  	switch ch {
   154  	case 'e', 'E':
   155  		// ok
   156  	case 'p':
   157  		if binExpOk {
   158  			base = 2
   159  			break // ok
   160  		}
   161  		fallthrough // binary exponent not permitted
   162  	default:
   163  		r.UnreadByte()
   164  		return // no exponent; same as e0
   165  	}
   166  
   167  	var neg bool
   168  	if neg, err = scanSign(r); err != nil {
   169  		return
   170  	}
   171  
   172  	var digits []byte
   173  	if neg {
   174  		digits = append(digits, '-')
   175  	}
   176  
   177  	// no need to use nat.scan for exponent digits
   178  	// since we only care about int64 values - the
   179  	// from-scratch scan is easy enough and faster
   180  	for i := 0; ; i++ {
   181  		if ch, err = r.ReadByte(); err != nil {
   182  			if err != io.EOF || i == 0 {
   183  				return
   184  			}
   185  			err = nil
   186  			break // i > 0
   187  		}
   188  		if ch < '0' || '9' < ch {
   189  			if i == 0 {
   190  				r.UnreadByte()
   191  				err = fmt.Errorf("invalid exponent (missing digits)")
   192  				return
   193  			}
   194  			break // i > 0
   195  		}
   196  		digits = append(digits, ch)
   197  	}
   198  	// i > 0 => we have at least one digit
   199  
   200  	exp, err = strconv.ParseInt(string(digits), 10, 64)
   201  	return
   202  }
   203  
   204  // String returns a string representation of x in the form "a/b" (even if b == 1).
   205  func (x *Rat) String() string {
   206  	return string(x.marshal())
   207  }
   208  
   209  // marshal implements String returning a slice of bytes
   210  func (x *Rat) marshal() []byte {
   211  	var buf []byte
   212  	buf = x.a.Append(buf, 10)
   213  	buf = append(buf, '/')
   214  	if len(x.b.abs) != 0 {
   215  		buf = x.b.Append(buf, 10)
   216  	} else {
   217  		buf = append(buf, '1')
   218  	}
   219  	return buf
   220  }
   221  
   222  // RatString returns a string representation of x in the form "a/b" if b != 1,
   223  // and in the form "a" if b == 1.
   224  func (x *Rat) RatString() string {
   225  	if x.IsInt() {
   226  		return x.a.String()
   227  	}
   228  	return x.String()
   229  }
   230  
   231  // FloatString returns a string representation of x in decimal form with prec
   232  // digits of precision after the decimal point. The last digit is rounded to
   233  // nearest, with halves rounded away from zero.
   234  func (x *Rat) FloatString(prec int) string {
   235  	var buf []byte
   236  
   237  	if x.IsInt() {
   238  		buf = x.a.Append(buf, 10)
   239  		if prec > 0 {
   240  			buf = append(buf, '.')
   241  			for i := prec; i > 0; i-- {
   242  				buf = append(buf, '0')
   243  			}
   244  		}
   245  		return string(buf)
   246  	}
   247  	// x.b.abs != 0
   248  
   249  	q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)
   250  
   251  	p := natOne
   252  	if prec > 0 {
   253  		p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
   254  	}
   255  
   256  	r = r.mul(r, p)
   257  	r, r2 := r.div(nat(nil), r, x.b.abs)
   258  
   259  	// see if we need to round up
   260  	r2 = r2.add(r2, r2)
   261  	if x.b.abs.cmp(r2) <= 0 {
   262  		r = r.add(r, natOne)
   263  		if r.cmp(p) >= 0 {
   264  			q = nat(nil).add(q, natOne)
   265  			r = nat(nil).sub(r, p)
   266  		}
   267  	}
   268  
   269  	if x.a.neg {
   270  		buf = append(buf, '-')
   271  	}
   272  	buf = append(buf, q.utoa(10)...) // itoa ignores sign if q == 0
   273  
   274  	if prec > 0 {
   275  		buf = append(buf, '.')
   276  		rs := r.utoa(10)
   277  		for i := prec - len(rs); i > 0; i-- {
   278  			buf = append(buf, '0')
   279  		}
   280  		buf = append(buf, rs...)
   281  	}
   282  
   283  	return string(buf)
   284  }
   285  

View as plain text