github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/src/math/big/ratconv.go (about)

     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  	var buf []byte
   206  	buf = x.a.Append(buf, 10)
   207  	buf = append(buf, '/')
   208  	if len(x.b.abs) != 0 {
   209  		buf = x.b.Append(buf, 10)
   210  	} else {
   211  		buf = append(buf, '1')
   212  	}
   213  	return string(buf)
   214  }
   215  
   216  // RatString returns a string representation of x in the form "a/b" if b != 1,
   217  // and in the form "a" if b == 1.
   218  func (x *Rat) RatString() string {
   219  	if x.IsInt() {
   220  		return x.a.String()
   221  	}
   222  	return x.String()
   223  }
   224  
   225  // FloatString returns a string representation of x in decimal form with prec
   226  // digits of precision after the decimal point. The last digit is rounded to
   227  // nearest, with halves rounded away from zero.
   228  func (x *Rat) FloatString(prec int) string {
   229  	var buf []byte
   230  
   231  	if x.IsInt() {
   232  		buf = x.a.Append(buf, 10)
   233  		if prec > 0 {
   234  			buf = append(buf, '.')
   235  			for i := prec; i > 0; i-- {
   236  				buf = append(buf, '0')
   237  			}
   238  		}
   239  		return string(buf)
   240  	}
   241  	// x.b.abs != 0
   242  
   243  	q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)
   244  
   245  	p := natOne
   246  	if prec > 0 {
   247  		p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
   248  	}
   249  
   250  	r = r.mul(r, p)
   251  	r, r2 := r.div(nat(nil), r, x.b.abs)
   252  
   253  	// see if we need to round up
   254  	r2 = r2.add(r2, r2)
   255  	if x.b.abs.cmp(r2) <= 0 {
   256  		r = r.add(r, natOne)
   257  		if r.cmp(p) >= 0 {
   258  			q = nat(nil).add(q, natOne)
   259  			r = nat(nil).sub(r, p)
   260  		}
   261  	}
   262  
   263  	if x.a.neg {
   264  		buf = append(buf, '-')
   265  	}
   266  	buf = append(buf, q.utoa(10)...) // itoa ignores sign if q == 0
   267  
   268  	if prec > 0 {
   269  		buf = append(buf, '.')
   270  		rs := r.utoa(10)
   271  		for i := prec - len(rs); i > 0; i-- {
   272  			buf = append(buf, '0')
   273  		}
   274  		buf = append(buf, rs...)
   275  	}
   276  
   277  	return string(buf)
   278  }