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.
15 func mkfloat(num string) float64 {
16 u, _ := strconv.ParseUint(num, 10, 32)
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) {
26 d.Convert(r, dec(num))
32 func (s dec) Convert(d *Decimal, _ RoundingContext) {
46 if p := strings.IndexAny(num, "eE"); p != -1 {
47 i64, err := strconv.ParseInt(num[p+1:], 10, 32)
54 if p := strings.IndexByte(num, '.'); p != -1 {
56 num = num[:p] + num[p+1:]
58 d.Exp += int32(len(num))
60 d.Digits = []byte(num)
61 for i := range d.Digits {
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' {
73 b[i] = s[i] - 'a' + 10
79 func strNum(s string) string {
80 return string(byteNum(s))
83 func TestDecimalString(t *testing.T) {
84 for _, test := range []struct {
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"},
95 if got := test.x.String(); got != test.want {
96 t.Errorf("%v == %q; want %q", test.x, got, test.want)
101 func TestRounding(t *testing.T) {
102 testCases := []struct {
105 // modes is the result for modes. Signs are left out of the result.
106 // The results are stored in the following order:
108 // nearZero, nearEven, nearAway
110 modes [numModes]string
112 {"0", 1, [numModes]string{
116 {"1", 1, [numModes]string{
120 {"5", 1, [numModes]string{
124 {"15", 1, [numModes]string{
128 {"45", 1, [numModes]string{
132 {"95", 1, [numModes]string{
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"}},
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"}},
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"}},
196 modes := []RoundingMode{
197 ToZero, ToNegativeInf,
198 ToNearestZero, ToNearestEven, ToNearestAway,
199 AwayFromZero, ToPositiveInf,
201 for _, tc := range testCases {
202 // Create negative counterpart tests: the sign is reversed and
203 // ToPositiveInf and ToNegativeInf swapped.
205 negModes[1], negModes[6] = negModes[6], negModes[1]
206 for i, res := range negModes {
207 negModes[i] = "-" + res
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) {
213 if got := d.String(); got != tc.modes[i] {
214 t.Errorf("pos decimal: got %q; want %q", d.String(), tc.modes[i])
217 mult := math.Pow(10, float64(len(tc.x)-tc.n))
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])
224 // Test the negative case. This is the same as the positive
225 // case, but with ToPositiveInf and ToNegativeInf swapped.
229 if got, want := d.String(), negModes[i]; got != want {
230 t.Errorf("neg decimal: got %q; want %q", d.String(), want)
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])
243 func TestConvert(t *testing.T) {
244 scale2 := RoundingContext{}
246 scale2away := RoundingContext{Mode: AwayFromZero}
247 scale2away.SetScale(2)
248 inc0_05 := RoundingContext{Increment: 5, IncrementScale: 2}
250 inc50 := RoundingContext{Increment: 50}
251 prec3 := RoundingContext{}
252 prec3.SetPrecision(3)
253 roundShift := RoundingContext{DigitShift: 2, MaxFractionDigits: 2}
254 testCases := []struct {
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"},
276 {0.03, inc0_05, "0.05"},
277 {0.025, inc0_05, "0.00"}, // not normalized
278 {0.075, inc0_05, "0.10"},
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"},
286 {converter(3), scale2, "100"},
288 {math.Inf(1), inc50, "Inf"},
289 {math.Inf(-1), inc50, "-Inf"},
290 {math.NaN(), inc50, "NaN"},
291 {"clearly not a number", scale2, "NaN"},
293 for _, tc := range testCases {
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)
306 func (c converter) Convert(d *Decimal, r RoundingContext) {
307 d.Digits = append(d.Digits, 1, 0, 0)