...
Run Format

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 floating-point number
    42  // 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  	var ecorr int
    82  	z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true)
    83  	if err != nil {
    84  		return nil, false
    85  	}
    86  
    87  	// exponent
    88  	var exp int64
    89  	exp, _, err = scanExponent(r, false)
    90  	if err != nil {
    91  		return nil, false
    92  	}
    93  
    94  	// there should be no unread characters left
    95  	if _, err = r.ReadByte(); err != io.EOF {
    96  		return nil, false
    97  	}
    98  
    99  	// special-case 0 (see also issue #16176)
   100  	if len(z.a.abs) == 0 {
   101  		return z, true
   102  	}
   103  	// len(z.a.abs) > 0
   104  
   105  	// correct exponent
   106  	if ecorr < 0 {
   107  		exp += int64(ecorr)
   108  	}
   109  
   110  	// compute exponent power
   111  	expabs := exp
   112  	if expabs < 0 {
   113  		expabs = -expabs
   114  	}
   115  	powTen := nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil)
   116  
   117  	// complete fraction
   118  	if exp < 0 {
   119  		z.b.abs = powTen
   120  		z.norm()
   121  	} else {
   122  		z.a.abs = z.a.abs.mul(z.a.abs, powTen)
   123  		z.b.abs = z.b.abs[:0]
   124  	}
   125  
   126  	z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign
   127  
   128  	return z, true
   129  }
   130  
   131  // scanExponent scans the longest possible prefix of r representing a decimal
   132  // ('e', 'E') or binary ('p') exponent, if any. It returns the exponent, the
   133  // exponent base (10 or 2), or a read or syntax error, if any.
   134  //
   135  //	exponent = ( "E" | "e" | "p" ) [ sign ] digits .
   136  //	sign     = "+" | "-" .
   137  //	digits   = digit { digit } .
   138  //	digit    = "0" ... "9" .
   139  //
   140  // A binary exponent is only permitted if binExpOk is set.
   141  func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err error) {
   142  	base = 10
   143  
   144  	var ch byte
   145  	if ch, err = r.ReadByte(); err != nil {
   146  		if err == io.EOF {
   147  			err = nil // no exponent; same as e0
   148  		}
   149  		return
   150  	}
   151  
   152  	switch ch {
   153  	case 'e', 'E':
   154  		// ok
   155  	case 'p':
   156  		if binExpOk {
   157  			base = 2
   158  			break // ok
   159  		}
   160  		fallthrough // binary exponent not permitted
   161  	default:
   162  		r.UnreadByte()
   163  		return // no exponent; same as e0
   164  	}
   165  
   166  	var neg bool
   167  	if neg, err = scanSign(r); err != nil {
   168  		return
   169  	}
   170  
   171  	var digits []byte
   172  	if neg {
   173  		digits = append(digits, '-')
   174  	}
   175  
   176  	// no need to use nat.scan for exponent digits
   177  	// since we only care about int64 values - the
   178  	// from-scratch scan is easy enough and faster
   179  	for i := 0; ; i++ {
   180  		if ch, err = r.ReadByte(); err != nil {
   181  			if err != io.EOF || i == 0 {
   182  				return
   183  			}
   184  			err = nil
   185  			break // i > 0
   186  		}
   187  		if ch < '0' || '9' < ch {
   188  			if i == 0 {
   189  				r.UnreadByte()
   190  				err = fmt.Errorf("invalid exponent (missing digits)")
   191  				return
   192  			}
   193  			break // i > 0
   194  		}
   195  		digits = append(digits, ch)
   196  	}
   197  	// i > 0 => we have at least one digit
   198  
   199  	exp, err = strconv.ParseInt(string(digits), 10, 64)
   200  	return
   201  }
   202  
   203  // String returns a string representation of x in the form "a/b" (even if b == 1).
   204  func (x *Rat) String() string {
   205  	return string(x.marshal())
   206  }
   207  
   208  // marshal implements String returning a slice of bytes
   209  func (x *Rat) marshal() []byte {
   210  	var buf []byte
   211  	buf = x.a.Append(buf, 10)
   212  	buf = append(buf, '/')
   213  	if len(x.b.abs) != 0 {
   214  		buf = x.b.Append(buf, 10)
   215  	} else {
   216  		buf = append(buf, '1')
   217  	}
   218  	return buf
   219  }
   220  
   221  // RatString returns a string representation of x in the form "a/b" if b != 1,
   222  // and in the form "a" if b == 1.
   223  func (x *Rat) RatString() string {
   224  	if x.IsInt() {
   225  		return x.a.String()
   226  	}
   227  	return x.String()
   228  }
   229  
   230  // FloatString returns a string representation of x in decimal form with prec
   231  // digits of precision after the decimal point. The last digit is rounded to
   232  // nearest, with halves rounded away from zero.
   233  func (x *Rat) FloatString(prec int) string {
   234  	var buf []byte
   235  
   236  	if x.IsInt() {
   237  		buf = x.a.Append(buf, 10)
   238  		if prec > 0 {
   239  			buf = append(buf, '.')
   240  			for i := prec; i > 0; i-- {
   241  				buf = append(buf, '0')
   242  			}
   243  		}
   244  		return string(buf)
   245  	}
   246  	// x.b.abs != 0
   247  
   248  	q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)
   249  
   250  	p := natOne
   251  	if prec > 0 {
   252  		p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
   253  	}
   254  
   255  	r = r.mul(r, p)
   256  	r, r2 := r.div(nat(nil), r, x.b.abs)
   257  
   258  	// see if we need to round up
   259  	r2 = r2.add(r2, r2)
   260  	if x.b.abs.cmp(r2) <= 0 {
   261  		r = r.add(r, natOne)
   262  		if r.cmp(p) >= 0 {
   263  			q = nat(nil).add(q, natOne)
   264  			r = nat(nil).sub(r, p)
   265  		}
   266  	}
   267  
   268  	if x.a.neg {
   269  		buf = append(buf, '-')
   270  	}
   271  	buf = append(buf, q.utoa(10)...) // itoa ignores sign if q == 0
   272  
   273  	if prec > 0 {
   274  		buf = append(buf, '.')
   275  		rs := r.utoa(10)
   276  		for i := prec - len(rs); i > 0; i-- {
   277  			buf = append(buf, '0')
   278  		}
   279  		buf = append(buf, rs...)
   280  	}
   281  
   282  	return string(buf)
   283  }
   284  

View as plain text