github.com/go-xe2/third@v1.0.3/golang.org/x/text/internal/number/decimal_test.go (about) 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 {-0.001, scale2, "-0.00"}, 260 {0.1234, prec3, "0.123"}, 261 {1234.0, prec3, "1230"}, 262 {1.2345e10, prec3, "12300000000"}, 263 264 {int8(-34), scale2, "-34"}, 265 {int16(-234), scale2, "-234"}, 266 {int32(-234), scale2, "-234"}, 267 {int64(-234), scale2, "-234"}, 268 {int(-234), scale2, "-234"}, 269 {uint8(234), scale2, "234"}, 270 {uint16(234), scale2, "234"}, 271 {uint32(234), scale2, "234"}, 272 {uint64(234), scale2, "234"}, 273 {uint(234), scale2, "234"}, 274 {-1e9, scale2, "-1000000000.00"}, 275 // The following two causes this result to have a lot of digits: 276 // 1) 0.234 cannot be accurately represented as a float64, and 277 // 2) as strconv does not support the rounding AwayFromZero, Convert 278 // leaves the rounding to caller. 279 {0.234, scale2away, 280 "0.2340000000000000135447209004269097931683063507080078125"}, 281 282 {0.0249, inc0_05, "0.00"}, 283 {0.025, inc0_05, "0.00"}, 284 {0.0251, inc0_05, "0.05"}, 285 {0.03, inc0_05, "0.05"}, 286 {0.049, inc0_05, "0.05"}, 287 {0.05, inc0_05, "0.05"}, 288 {0.051, inc0_05, "0.05"}, 289 {0.0749, inc0_05, "0.05"}, 290 {0.075, inc0_05, "0.10"}, 291 {0.0751, inc0_05, "0.10"}, 292 {324, inc50, "300"}, 293 {325, inc50, "300"}, 294 {326, inc50, "350"}, 295 {349, inc50, "350"}, 296 {350, inc50, "350"}, 297 {351, inc50, "350"}, 298 {374, inc50, "350"}, 299 {375, inc50, "400"}, 300 {376, inc50, "400"}, 301 302 // Here the scale is 2, but the digits get shifted left. As we use 303 // AppendFloat to do the rounding an exta 0 gets added. 304 {0.123, roundShift, "0.1230"}, 305 306 {converter(3), scale2, "100"}, 307 308 {math.Inf(1), inc50, "Inf"}, 309 {math.Inf(-1), inc50, "-Inf"}, 310 {math.NaN(), inc50, "NaN"}, 311 {"clearly not a number", scale2, "NaN"}, 312 } 313 for _, tc := range testCases { 314 var d Decimal 315 t.Run(fmt.Sprintf("%T:%v-%v", tc.x, tc.x, tc.rc), func(t *testing.T) { 316 d.Convert(tc.rc, tc.x) 317 if got := d.String(); got != tc.out { 318 t.Errorf("got %q; want %q", got, tc.out) 319 } 320 }) 321 } 322 } 323 324 type converter int 325 326 func (c converter) Convert(d *Decimal, r RoundingContext) { 327 d.Digits = append(d.Digits, 1, 0, 0) 328 d.Exp = 3 329 }