1
2
3
4
5
6
7 package number
8
9 import (
10 "math"
11 "strconv"
12 )
13
14
15 type RoundingMode byte
16
17 const (
18 ToNearestEven RoundingMode = iota
19 ToNearestZero
20 ToNearestAway
21 ToPositiveInf
22 ToNegativeInf
23 ToZero
24 AwayFromZero
25 numModes
26 )
27
28 const maxIntDigits = 20
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 type Decimal struct {
45 digits
46
47 buf [maxIntDigits]byte
48 }
49
50 type digits struct {
51 Digits []byte
52 Exp int32
53 Neg bool
54 Inf bool
55 NaN bool
56 }
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 type Digits struct {
82 digits
83
84 End int32
85
86
87 Comma uint8
88
89
90 IsScientific bool
91 }
92
93 func (d *Digits) NumFracDigits() int {
94 if d.Exp >= d.End {
95 return 0
96 }
97 return int(d.End - d.Exp)
98 }
99
100
101 func (d *Decimal) normalize() (n Decimal) {
102 n = *d
103 b := n.Digits
104
105 for len(b) > 0 && b[0] == 0 {
106 b = b[1:]
107 n.Exp--
108 }
109
110 for len(b) > 0 && b[len(b)-1] == 0 {
111 b = b[:len(b)-1]
112 }
113 if len(b) == 0 {
114 n.Exp = 0
115 }
116 n.Digits = b
117 return n
118 }
119
120 func (d *Decimal) clear() {
121 b := d.Digits
122 if b == nil {
123 b = d.buf[:0]
124 }
125 *d = Decimal{}
126 d.Digits = b[:0]
127 }
128
129 func (x *Decimal) String() string {
130 if x.NaN {
131 return "NaN"
132 }
133 var buf []byte
134 if x.Neg {
135 buf = append(buf, '-')
136 }
137 if x.Inf {
138 buf = append(buf, "Inf"...)
139 return string(buf)
140 }
141 switch {
142 case len(x.Digits) == 0:
143 buf = append(buf, '0')
144 case x.Exp <= 0:
145
146 buf = append(buf, "0."...)
147 buf = appendZeros(buf, -int(x.Exp))
148 buf = appendDigits(buf, x.Digits)
149
150 case int(x.Exp) < len(x.Digits):
151
152 buf = appendDigits(buf, x.Digits[:x.Exp])
153 buf = append(buf, '.')
154 buf = appendDigits(buf, x.Digits[x.Exp:])
155
156 default:
157
158 buf = appendDigits(buf, x.Digits)
159 buf = appendZeros(buf, int(x.Exp)-len(x.Digits))
160 }
161 return string(buf)
162 }
163
164 func appendDigits(buf []byte, digits []byte) []byte {
165 for _, c := range digits {
166 buf = append(buf, c+'0')
167 }
168 return buf
169 }
170
171
172 func appendZeros(buf []byte, n int) []byte {
173 for ; n > 0; n-- {
174 buf = append(buf, '0')
175 }
176 return buf
177 }
178
179 func (d *digits) round(mode RoundingMode, n int) {
180 if n >= len(d.Digits) {
181 return
182 }
183
184
185
186 inc := false
187 switch mode {
188 case ToNegativeInf:
189 inc = d.Neg
190 case ToPositiveInf:
191 inc = !d.Neg
192 case ToZero:
193
194 case AwayFromZero:
195 inc = true
196 case ToNearestEven:
197 inc = d.Digits[n] > 5 || d.Digits[n] == 5 &&
198 (len(d.Digits) > n+1 || n == 0 || d.Digits[n-1]&1 != 0)
199 case ToNearestAway:
200 inc = d.Digits[n] >= 5
201 case ToNearestZero:
202 inc = d.Digits[n] > 5 || d.Digits[n] == 5 && len(d.Digits) > n+1
203 default:
204 panic("unreachable")
205 }
206 if inc {
207 d.roundUp(n)
208 } else {
209 d.roundDown(n)
210 }
211 }
212
213
214 func (r RoundingMode) roundFloat(x float64) float64 {
215
216
217
218 abs := x
219 if x < 0 {
220 abs = -x
221 }
222 i, f := math.Modf(abs)
223 if f == 0.0 {
224 return x
225 }
226 inc := false
227 switch r {
228 case ToNegativeInf:
229 inc = x < 0
230 case ToPositiveInf:
231 inc = x >= 0
232 case ToZero:
233
234 case AwayFromZero:
235 inc = true
236 case ToNearestEven:
237
238 inc = f > 0.5 || f == 0.5 && int64(i)&1 != 0
239 case ToNearestAway:
240 inc = f >= 0.5
241 case ToNearestZero:
242 inc = f > 0.5
243 default:
244 panic("unreachable")
245 }
246 if inc {
247 i += 1
248 }
249 if abs != x {
250 i = -i
251 }
252 return i
253 }
254
255 func (x *digits) roundUp(n int) {
256 if n < 0 || n >= len(x.Digits) {
257 return
258 }
259
260 for n > 0 && x.Digits[n-1] >= 9 {
261 n--
262 }
263
264 if n == 0 {
265
266 x.Digits[0] = 1
267 x.Digits = x.Digits[:1]
268 x.Exp++
269 return
270 }
271 x.Digits[n-1]++
272 x.Digits = x.Digits[:n]
273
274 }
275
276 func (x *digits) roundDown(n int) {
277 if n < 0 || n >= len(x.Digits) {
278 return
279 }
280 x.Digits = x.Digits[:n]
281 trim(x)
282 }
283
284
285
286 func trim(x *digits) {
287 i := len(x.Digits)
288 for i > 0 && x.Digits[i-1] == 0 {
289 i--
290 }
291 x.Digits = x.Digits[:i]
292 if i == 0 {
293 x.Exp = 0
294 }
295 }
296
297
298
299 type Converter interface {
300 Convert(d *Decimal, r RoundingContext)
301 }
302
303 const (
304 signed = true
305 unsigned = false
306 )
307
308
309
310 func (d *Decimal) Convert(r RoundingContext, number interface{}) {
311 switch f := number.(type) {
312 case Converter:
313 d.clear()
314 f.Convert(d, r)
315 case float32:
316 d.ConvertFloat(r, float64(f), 32)
317 case float64:
318 d.ConvertFloat(r, f, 64)
319 case int:
320 d.ConvertInt(r, signed, uint64(f))
321 case int8:
322 d.ConvertInt(r, signed, uint64(f))
323 case int16:
324 d.ConvertInt(r, signed, uint64(f))
325 case int32:
326 d.ConvertInt(r, signed, uint64(f))
327 case int64:
328 d.ConvertInt(r, signed, uint64(f))
329 case uint:
330 d.ConvertInt(r, unsigned, uint64(f))
331 case uint8:
332 d.ConvertInt(r, unsigned, uint64(f))
333 case uint16:
334 d.ConvertInt(r, unsigned, uint64(f))
335 case uint32:
336 d.ConvertInt(r, unsigned, uint64(f))
337 case uint64:
338 d.ConvertInt(r, unsigned, f)
339
340 default:
341 d.NaN = true
342
343
344
345
346
347
348
349
350 }
351 }
352
353
354 func (d *Decimal) ConvertInt(r RoundingContext, signed bool, x uint64) {
355 if r.Increment > 0 {
356
357 if signed {
358 d.ConvertFloat(r, float64(int64(x)), 64)
359 } else {
360 d.ConvertFloat(r, float64(x), 64)
361 }
362 return
363 }
364 d.clear()
365 if signed && int64(x) < 0 {
366 x = uint64(-int64(x))
367 d.Neg = true
368 }
369 d.fillIntDigits(x)
370 d.Exp = int32(len(d.Digits))
371 }
372
373
374 func (d *Decimal) ConvertFloat(r RoundingContext, x float64, size int) {
375 d.clear()
376 if math.IsNaN(x) {
377 d.NaN = true
378 return
379 }
380
381 if r.Increment > 0 {
382 scale := int(r.IncrementScale)
383 mult := 1.0
384 if scale >= len(scales) {
385 mult = math.Pow(10, float64(scale))
386 } else {
387 mult = scales[scale]
388 }
389
390
391 x *= mult
392 x /= float64(r.Increment)
393 x = r.Mode.roundFloat(x)
394 x *= float64(r.Increment)
395 x /= mult
396 }
397
398 abs := x
399 if x < 0 {
400 d.Neg = true
401 abs = -x
402 }
403 if math.IsInf(abs, 1) {
404 d.Inf = true
405 return
406 }
407
408
409 verb := byte('g')
410 prec := -1
411
412
413 if r.Mode == ToNearestEven {
414 if n := r.RoundSignificantDigits(); n >= 0 {
415 prec = n
416 } else if n = r.RoundFractionDigits(); n >= 0 {
417 prec = n
418 verb = 'f'
419 }
420 } else {
421
422
423
424
425
426
427
428
429
430 hasPrec := r.RoundSignificantDigits() >= 0
431 hasScale := r.RoundFractionDigits() >= 0
432 if hasPrec || hasScale {
433
434
435
436
437 prec = 60
438 }
439 }
440
441 b := strconv.AppendFloat(d.Digits[:0], abs, verb, prec, size)
442 i := 0
443 k := 0
444 beforeDot := 1
445 for i < len(b) {
446 if c := b[i]; '0' <= c && c <= '9' {
447 b[k] = c - '0'
448 k++
449 d.Exp += int32(beforeDot)
450 } else if c == '.' {
451 beforeDot = 0
452 d.Exp = int32(k)
453 } else {
454 break
455 }
456 i++
457 }
458 d.Digits = b[:k]
459 if i != len(b) {
460 i += len("e")
461 pSign := i
462 exp := 0
463 for i++; i < len(b); i++ {
464 exp *= 10
465 exp += int(b[i] - '0')
466 }
467 if b[pSign] == '-' {
468 exp = -exp
469 }
470 d.Exp = int32(exp) + 1
471 }
472 }
473
474 func (d *Decimal) fillIntDigits(x uint64) {
475 if cap(d.Digits) < maxIntDigits {
476 d.Digits = d.buf[:]
477 } else {
478 d.Digits = d.buf[:maxIntDigits]
479 }
480 i := 0
481 for ; x > 0; x /= 10 {
482 d.Digits[i] = byte(x % 10)
483 i++
484 }
485 d.Digits = d.Digits[:i]
486 for p := 0; p < i; p++ {
487 i--
488 d.Digits[p], d.Digits[i] = d.Digits[i], d.Digits[p]
489 }
490 }
491
492 var scales [70]float64
493
494 func init() {
495 x := 1.0
496 for i := range scales {
497 scales[i] = x
498 x *= 10
499 }
500 }
501
View as plain text