OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / text / internal / number / decimal_test.go
1 // Copyright 2017 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 package number
6
7 import (
8         "fmt"
9         "math"
10         "strconv"
11         "strings"
12         "testing"
13 )
14
15 func mkfloat(num string) float64 {
16         u, _ := strconv.ParseUint(num, 10, 32)
17         return float64(u)
18 }
19
20 // mkdec creates a decimal from a string. All ASCII digits are converted to
21 // digits in the decimal. The dot is used to indicate the scale by which the
22 // digits are shifted. Numbers may have an additional exponent or be the special
23 // value NaN, Inf, or -Inf.
24 func mkdec(num string) (d Decimal) {
25         var r RoundingContext
26         d.Convert(r, dec(num))
27         return
28 }
29
30 type dec string
31
32 func (s dec) Convert(d *Decimal, _ RoundingContext) {
33         num := string(s)
34         if num[0] == '-' {
35                 d.Neg = true
36                 num = num[1:]
37         }
38         switch num {
39         case "NaN":
40                 d.NaN = true
41                 return
42         case "Inf":
43                 d.Inf = true
44                 return
45         }
46         if p := strings.IndexAny(num, "eE"); p != -1 {
47                 i64, err := strconv.ParseInt(num[p+1:], 10, 32)
48                 if err != nil {
49                         panic(err)
50                 }
51                 d.Exp = int32(i64)
52                 num = num[:p]
53         }
54         if p := strings.IndexByte(num, '.'); p != -1 {
55                 d.Exp += int32(p)
56                 num = num[:p] + num[p+1:]
57         } else {
58                 d.Exp += int32(len(num))
59         }
60         d.Digits = []byte(num)
61         for i := range d.Digits {
62                 d.Digits[i] -= '0'
63         }
64         *d = d.normalize()
65 }
66
67 func byteNum(s string) []byte {
68         b := make([]byte, len(s))
69         for i := 0; i < len(s); i++ {
70                 if c := s[i]; '0' <= c && c <= '9' {
71                         b[i] = s[i] - '0'
72                 } else {
73                         b[i] = s[i] - 'a' + 10
74                 }
75         }
76         return b
77 }
78
79 func strNum(s string) string {
80         return string(byteNum(s))
81 }
82
83 func TestDecimalString(t *testing.T) {
84         for _, test := range []struct {
85                 x    Decimal
86                 want string
87         }{
88                 {want: "0"},
89                 {Decimal{digits: digits{Digits: nil, Exp: 1000}}, "0"}, // exponent of 1000 is ignored
90                 {Decimal{digits: digits{Digits: byteNum("12345"), Exp: 0}}, "0.12345"},
91                 {Decimal{digits: digits{Digits: byteNum("12345"), Exp: -3}}, "0.00012345"},
92                 {Decimal{digits: digits{Digits: byteNum("12345"), Exp: +3}}, "123.45"},
93                 {Decimal{digits: digits{Digits: byteNum("12345"), Exp: +10}}, "1234500000"},
94         } {
95                 if got := test.x.String(); got != test.want {
96                         t.Errorf("%v == %q; want %q", test.x, got, test.want)
97                 }
98         }
99 }
100
101 func TestRounding(t *testing.T) {
102         testCases := []struct {
103                 x string
104                 n int
105                 // modes is the result for modes. Signs are left out of the result.
106                 // The results are stored in the following order:
107                 // zero, negInf
108                 // nearZero, nearEven, nearAway
109                 // away, posInf
110                 modes [numModes]string
111         }{
112                 {"0", 1, [numModes]string{
113                         "0", "0",
114                         "0", "0", "0",
115                         "0", "0"}},
116                 {"1", 1, [numModes]string{
117                         "1", "1",
118                         "1", "1", "1",
119                         "1", "1"}},
120                 {"5", 1, [numModes]string{
121                         "5", "5",
122                         "5", "5", "5",
123                         "5", "5"}},
124                 {"15", 1, [numModes]string{
125                         "10", "10",
126                         "10", "20", "20",
127                         "20", "20"}},
128                 {"45", 1, [numModes]string{
129                         "40", "40",
130                         "40", "40", "50",
131                         "50", "50"}},
132                 {"95", 1, [numModes]string{
133                         "90", "90",
134                         "90", "100", "100",
135                         "100", "100"}},
136
137                 {"12344999", 4, [numModes]string{
138                         "12340000", "12340000",
139                         "12340000", "12340000", "12340000",
140                         "12350000", "12350000"}},
141                 {"12345000", 4, [numModes]string{
142                         "12340000", "12340000",
143                         "12340000", "12340000", "12350000",
144                         "12350000", "12350000"}},
145                 {"12345001", 4, [numModes]string{
146                         "12340000", "12340000",
147                         "12350000", "12350000", "12350000",
148                         "12350000", "12350000"}},
149                 {"12345100", 4, [numModes]string{
150                         "12340000", "12340000",
151                         "12350000", "12350000", "12350000",
152                         "12350000", "12350000"}},
153                 {"23454999", 4, [numModes]string{
154                         "23450000", "23450000",
155                         "23450000", "23450000", "23450000",
156                         "23460000", "23460000"}},
157                 {"23455000", 4, [numModes]string{
158                         "23450000", "23450000",
159                         "23450000", "23460000", "23460000",
160                         "23460000", "23460000"}},
161                 {"23455001", 4, [numModes]string{
162                         "23450000", "23450000",
163                         "23460000", "23460000", "23460000",
164                         "23460000", "23460000"}},
165                 {"23455100", 4, [numModes]string{
166                         "23450000", "23450000",
167                         "23460000", "23460000", "23460000",
168                         "23460000", "23460000"}},
169
170                 {"99994999", 4, [numModes]string{
171                         "99990000", "99990000",
172                         "99990000", "99990000", "99990000",
173                         "100000000", "100000000"}},
174                 {"99995000", 4, [numModes]string{
175                         "99990000", "99990000",
176                         "99990000", "100000000", "100000000",
177                         "100000000", "100000000"}},
178                 {"99999999", 4, [numModes]string{
179                         "99990000", "99990000",
180                         "100000000", "100000000", "100000000",
181                         "100000000", "100000000"}},
182
183                 {"12994999", 4, [numModes]string{
184                         "12990000", "12990000",
185                         "12990000", "12990000", "12990000",
186                         "13000000", "13000000"}},
187                 {"12995000", 4, [numModes]string{
188                         "12990000", "12990000",
189                         "12990000", "13000000", "13000000",
190                         "13000000", "13000000"}},
191                 {"12999999", 4, [numModes]string{
192                         "12990000", "12990000",
193                         "13000000", "13000000", "13000000",
194                         "13000000", "13000000"}},
195         }
196         modes := []RoundingMode{
197                 ToZero, ToNegativeInf,
198                 ToNearestZero, ToNearestEven, ToNearestAway,
199                 AwayFromZero, ToPositiveInf,
200         }
201         for _, tc := range testCases {
202                 // Create negative counterpart tests: the sign is reversed and
203                 // ToPositiveInf and ToNegativeInf swapped.
204                 negModes := tc.modes
205                 negModes[1], negModes[6] = negModes[6], negModes[1]
206                 for i, res := range negModes {
207                         negModes[i] = "-" + res
208                 }
209                 for i, m := range modes {
210                         t.Run(fmt.Sprintf("x:%s/n:%d/%s", tc.x, tc.n, m), func(t *testing.T) {
211                                 d := mkdec(tc.x)
212                                 d.round(m, tc.n)
213                                 if got := d.String(); got != tc.modes[i] {
214                                         t.Errorf("pos decimal: got %q; want %q", d.String(), tc.modes[i])
215                                 }
216
217                                 mult := math.Pow(10, float64(len(tc.x)-tc.n))
218                                 f := mkfloat(tc.x)
219                                 f = m.roundFloat(f/mult) * mult
220                                 if got := fmt.Sprintf("%.0f", f); got != tc.modes[i] {
221                                         t.Errorf("pos float: got %q; want %q", got, tc.modes[i])
222                                 }
223
224                                 // Test the negative case. This is the same as the positive
225                                 // case, but with ToPositiveInf and ToNegativeInf swapped.
226                                 d = mkdec(tc.x)
227                                 d.Neg = true
228                                 d.round(m, tc.n)
229                                 if got, want := d.String(), negModes[i]; got != want {
230                                         t.Errorf("neg decimal: got %q; want %q", d.String(), want)
231                                 }
232
233                                 f = -mkfloat(tc.x)
234                                 f = m.roundFloat(f/mult) * mult
235                                 if got := fmt.Sprintf("%.0f", f); got != negModes[i] {
236                                         t.Errorf("neg float: got %q; want %q", got, negModes[i])
237                                 }
238                         })
239                 }
240         }
241 }
242
243 func TestConvert(t *testing.T) {
244         scale2 := RoundingContext{}
245         scale2.SetScale(2)
246         scale2away := RoundingContext{Mode: AwayFromZero}
247         scale2away.SetScale(2)
248         inc0_05 := RoundingContext{Increment: 5, IncrementScale: 2}
249         inc0_05.SetScale(2)
250         inc50 := RoundingContext{Increment: 50}
251         prec3 := RoundingContext{}
252         prec3.SetPrecision(3)
253         roundShift := RoundingContext{DigitShift: 2, MaxFractionDigits: 2}
254         testCases := []struct {
255                 x   interface{}
256                 rc  RoundingContext
257                 out string
258         }{
259                 {int8(-34), scale2, "-34"},
260                 {int16(-234), scale2, "-234"},
261                 {int32(-234), scale2, "-234"},
262                 {int64(-234), scale2, "-234"},
263                 {int(-234), scale2, "-234"},
264                 {uint8(234), scale2, "234"},
265                 {uint16(234), scale2, "234"},
266                 {uint32(234), scale2, "234"},
267                 {uint64(234), scale2, "234"},
268                 {uint(234), scale2, "234"},
269                 {-0.001, scale2, "-0.00"}, // not normalized
270                 {-1e9, scale2, "-1000000000.00"},
271                 {0.234, scale2away, "0.234"}, // rounding postponed as not ToNearestEven
272                 {0.1234, prec3, "0.123"},
273                 {1234.0, prec3, "1230"},
274                 {1.2345e10, prec3, "12300000000"},
275
276                 {0.03, inc0_05, "0.05"},
277                 {0.025, inc0_05, "0.00"}, // not normalized
278                 {0.075, inc0_05, "0.10"},
279                 {325, inc50, "300"},
280                 {375, inc50, "400"},
281
282                 // Here the scale is 2, but the digits get shifted left. As we use
283                 // AppendFloat to do the rounding an exta 0 gets added.
284                 {0.123, roundShift, "0.1230"},
285
286                 {converter(3), scale2, "100"},
287
288                 {math.Inf(1), inc50, "Inf"},
289                 {math.Inf(-1), inc50, "-Inf"},
290                 {math.NaN(), inc50, "NaN"},
291                 {"clearly not a number", scale2, "NaN"},
292         }
293         for _, tc := range testCases {
294                 var d Decimal
295                 t.Run(fmt.Sprintf("%T:%v-%v", tc.x, tc.x, tc.rc), func(t *testing.T) {
296                         d.Convert(tc.rc, tc.x)
297                         if got := d.String(); got != tc.out {
298                                 t.Errorf("got %q; want %q", got, tc.out)
299                         }
300                 })
301         }
302 }
303
304 type converter int
305
306 func (c converter) Convert(d *Decimal, r RoundingContext) {
307         d.Digits = append(d.Digits, 1, 0, 0)
308         d.Exp = 3
309 }