Home | History | Annotate | Download | only in 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