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