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 }