]> Cypherpunks.ru repositories - gostls13.git/blob - src/math/big/ratconv.go
math/big: add support for underscores '_' in numbers
[gostls13.git] / src / math / big / ratconv.go
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 decimal floating-point
42 // number 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         // TODO(gri) allow other bases besides 10 for mantissa and exponent? (issue #29799)
82         var ecorr int
83         z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true)
84         if err != nil {
85                 return nil, false
86         }
87
88         // exponent
89         var exp int64
90         exp, _, err = scanExponent(r, false, false)
91         if err != nil {
92                 return nil, false
93         }
94
95         // there should be no unread characters left
96         if _, err = r.ReadByte(); err != io.EOF {
97                 return nil, false
98         }
99
100         // special-case 0 (see also issue #16176)
101         if len(z.a.abs) == 0 {
102                 return z, true
103         }
104         // len(z.a.abs) > 0
105
106         // correct exponent
107         if ecorr < 0 {
108                 exp += int64(ecorr)
109         }
110
111         // compute exponent power
112         expabs := exp
113         if expabs < 0 {
114                 expabs = -expabs
115         }
116         powTen := nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil)
117
118         // complete fraction
119         if exp < 0 {
120                 z.b.abs = powTen
121                 z.norm()
122         } else {
123                 z.a.abs = z.a.abs.mul(z.a.abs, powTen)
124                 z.b.abs = z.b.abs[:0]
125         }
126
127         z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign
128
129         return z, true
130 }
131
132 // scanExponent scans the longest possible prefix of r representing a base 10
133 // (``e'', ``E'') or a base 2 (``p'', ``P'') exponent, if any. It returns the
134 // exponent, the exponent base (10 or 2), or a read or syntax error, if any.
135 //
136 // If sepOk is set, an underscore character ``_'' may appear between successive
137 // exponent digits; such underscores do not change the value of the exponent.
138 // Incorrect placement of underscores is reported as an error if there are no
139 // other errors. If sepOk is not set, underscores are not recognized and thus
140 // terminate scanning like any other character that is not a valid digit.
141 //
142 //      exponent = ( "e" | "E" | "p" | "P" ) [ sign ] digits .
143 //      sign     = "+" | "-" .
144 //      digits   = digit { [ '_' ] digit } .
145 //      digit    = "0" ... "9" .
146 //
147 // A base 2 exponent is only permitted if base2ok is set.
148 func scanExponent(r io.ByteScanner, base2ok, sepOk bool) (exp int64, base int, err error) {
149         // one char look-ahead
150         ch, err := r.ReadByte()
151         if err != nil {
152                 if err == io.EOF {
153                         err = nil
154                 }
155                 return 0, 10, err
156         }
157
158         // exponent char
159         switch ch {
160         case 'e', 'E':
161                 base = 10
162         case 'p', 'P':
163                 if base2ok {
164                         base = 2
165                         break // ok
166                 }
167                 fallthrough // binary exponent not permitted
168         default:
169                 r.UnreadByte() // ch does not belong to exponent anymore
170                 return 0, 10, nil
171         }
172
173         // sign
174         var digits []byte
175         ch, err = r.ReadByte()
176         if err == nil && (ch == '+' || ch == '-') {
177                 if ch == '-' {
178                         digits = append(digits, '-')
179                 }
180                 ch, err = r.ReadByte()
181         }
182
183         // prev encodes the previously seen char: it is one
184         // of '_', '0' (a digit), or '.' (anything else). A
185         // valid separator '_' may only occur after a digit.
186         prev := '.'
187         invalSep := false
188
189         // exponent value
190         hasDigits := false
191         for err == nil {
192                 if '0' <= ch && ch <= '9' {
193                         digits = append(digits, ch)
194                         prev = '0'
195                         hasDigits = true
196                 } else if ch == '_' && sepOk {
197                         if prev != '0' {
198                                 invalSep = true
199                         }
200                         prev = '_'
201                 } else {
202                         r.UnreadByte() // ch does not belong to number anymore
203                         break
204                 }
205                 ch, err = r.ReadByte()
206         }
207
208         if err == io.EOF {
209                 err = nil
210         }
211         if err == nil && !hasDigits {
212                 err = errNoDigits
213         }
214         if err == nil {
215                 exp, err = strconv.ParseInt(string(digits), 10, 64)
216         }
217         // other errors take precedence over invalid separators
218         if err == nil && (invalSep || prev == '_') {
219                 err = errInvalSep
220         }
221
222         return
223 }
224
225 // String returns a string representation of x in the form "a/b" (even if b == 1).
226 func (x *Rat) String() string {
227         return string(x.marshal())
228 }
229
230 // marshal implements String returning a slice of bytes
231 func (x *Rat) marshal() []byte {
232         var buf []byte
233         buf = x.a.Append(buf, 10)
234         buf = append(buf, '/')
235         if len(x.b.abs) != 0 {
236                 buf = x.b.Append(buf, 10)
237         } else {
238                 buf = append(buf, '1')
239         }
240         return buf
241 }
242
243 // RatString returns a string representation of x in the form "a/b" if b != 1,
244 // and in the form "a" if b == 1.
245 func (x *Rat) RatString() string {
246         if x.IsInt() {
247                 return x.a.String()
248         }
249         return x.String()
250 }
251
252 // FloatString returns a string representation of x in decimal form with prec
253 // digits of precision after the decimal point. The last digit is rounded to
254 // nearest, with halves rounded away from zero.
255 func (x *Rat) FloatString(prec int) string {
256         var buf []byte
257
258         if x.IsInt() {
259                 buf = x.a.Append(buf, 10)
260                 if prec > 0 {
261                         buf = append(buf, '.')
262                         for i := prec; i > 0; i-- {
263                                 buf = append(buf, '0')
264                         }
265                 }
266                 return string(buf)
267         }
268         // x.b.abs != 0
269
270         q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)
271
272         p := natOne
273         if prec > 0 {
274                 p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
275         }
276
277         r = r.mul(r, p)
278         r, r2 := r.div(nat(nil), r, x.b.abs)
279
280         // see if we need to round up
281         r2 = r2.add(r2, r2)
282         if x.b.abs.cmp(r2) <= 0 {
283                 r = r.add(r, natOne)
284                 if r.cmp(p) >= 0 {
285                         q = nat(nil).add(q, natOne)
286                         r = nat(nil).sub(r, p)
287                 }
288         }
289
290         if x.a.neg {
291                 buf = append(buf, '-')
292         }
293         buf = append(buf, q.utoa(10)...) // itoa ignores sign if q == 0
294
295         if prec > 0 {
296                 buf = append(buf, '.')
297                 rs := r.utoa(10)
298                 for i := prec - len(rs); i > 0; i-- {
299                         buf = append(buf, '0')
300                 }
301                 buf = append(buf, rs...)
302         }
303
304         return string(buf)
305 }