summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/text/internal/number/decimal_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/text/internal/number/decimal_test.go')
-rw-r--r--vendor/golang.org/x/text/internal/number/decimal_test.go329
1 files changed, 329 insertions, 0 deletions
diff --git a/vendor/golang.org/x/text/internal/number/decimal_test.go b/vendor/golang.org/x/text/internal/number/decimal_test.go
new file mode 100644
index 0000000..97c7e25
--- /dev/null
+++ b/vendor/golang.org/x/text/internal/number/decimal_test.go
@@ -0,0 +1,329 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package number
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func mkfloat(num string) float64 {
+ u, _ := strconv.ParseUint(num, 10, 32)
+ return float64(u)
+}
+
+// mkdec creates a decimal from a string. All ASCII digits are converted to
+// digits in the decimal. The dot is used to indicate the scale by which the
+// digits are shifted. Numbers may have an additional exponent or be the special
+// value NaN, Inf, or -Inf.
+func mkdec(num string) (d Decimal) {
+ var r RoundingContext
+ d.Convert(r, dec(num))
+ return
+}
+
+type dec string
+
+func (s dec) Convert(d *Decimal, _ RoundingContext) {
+ num := string(s)
+ if num[0] == '-' {
+ d.Neg = true
+ num = num[1:]
+ }
+ switch num {
+ case "NaN":
+ d.NaN = true
+ return
+ case "Inf":
+ d.Inf = true
+ return
+ }
+ if p := strings.IndexAny(num, "eE"); p != -1 {
+ i64, err := strconv.ParseInt(num[p+1:], 10, 32)
+ if err != nil {
+ panic(err)
+ }
+ d.Exp = int32(i64)
+ num = num[:p]
+ }
+ if p := strings.IndexByte(num, '.'); p != -1 {
+ d.Exp += int32(p)
+ num = num[:p] + num[p+1:]
+ } else {
+ d.Exp += int32(len(num))
+ }
+ d.Digits = []byte(num)
+ for i := range d.Digits {
+ d.Digits[i] -= '0'
+ }
+ *d = d.normalize()
+}
+
+func byteNum(s string) []byte {
+ b := make([]byte, len(s))
+ for i := 0; i < len(s); i++ {
+ if c := s[i]; '0' <= c && c <= '9' {
+ b[i] = s[i] - '0'
+ } else {
+ b[i] = s[i] - 'a' + 10
+ }
+ }
+ return b
+}
+
+func strNum(s string) string {
+ return string(byteNum(s))
+}
+
+func TestDecimalString(t *testing.T) {
+ for _, test := range []struct {
+ x Decimal
+ want string
+ }{
+ {want: "0"},
+ {Decimal{digits: digits{Digits: nil, Exp: 1000}}, "0"}, // exponent of 1000 is ignored
+ {Decimal{digits: digits{Digits: byteNum("12345"), Exp: 0}}, "0.12345"},
+ {Decimal{digits: digits{Digits: byteNum("12345"), Exp: -3}}, "0.00012345"},
+ {Decimal{digits: digits{Digits: byteNum("12345"), Exp: +3}}, "123.45"},
+ {Decimal{digits: digits{Digits: byteNum("12345"), Exp: +10}}, "1234500000"},
+ } {
+ if got := test.x.String(); got != test.want {
+ t.Errorf("%v == %q; want %q", test.x, got, test.want)
+ }
+ }
+}
+
+func TestRounding(t *testing.T) {
+ testCases := []struct {
+ x string
+ n int
+ // modes is the result for modes. Signs are left out of the result.
+ // The results are stored in the following order:
+ // zero, negInf
+ // nearZero, nearEven, nearAway
+ // away, posInf
+ modes [numModes]string
+ }{
+ {"0", 1, [numModes]string{
+ "0", "0",
+ "0", "0", "0",
+ "0", "0"}},
+ {"1", 1, [numModes]string{
+ "1", "1",
+ "1", "1", "1",
+ "1", "1"}},
+ {"5", 1, [numModes]string{
+ "5", "5",
+ "5", "5", "5",
+ "5", "5"}},
+ {"15", 1, [numModes]string{
+ "10", "10",
+ "10", "20", "20",
+ "20", "20"}},
+ {"45", 1, [numModes]string{
+ "40", "40",
+ "40", "40", "50",
+ "50", "50"}},
+ {"95", 1, [numModes]string{
+ "90", "90",
+ "90", "100", "100",
+ "100", "100"}},
+
+ {"12344999", 4, [numModes]string{
+ "12340000", "12340000",
+ "12340000", "12340000", "12340000",
+ "12350000", "12350000"}},
+ {"12345000", 4, [numModes]string{
+ "12340000", "12340000",
+ "12340000", "12340000", "12350000",
+ "12350000", "12350000"}},
+ {"12345001", 4, [numModes]string{
+ "12340000", "12340000",
+ "12350000", "12350000", "12350000",
+ "12350000", "12350000"}},
+ {"12345100", 4, [numModes]string{
+ "12340000", "12340000",
+ "12350000", "12350000", "12350000",
+ "12350000", "12350000"}},
+ {"23454999", 4, [numModes]string{
+ "23450000", "23450000",
+ "23450000", "23450000", "23450000",
+ "23460000", "23460000"}},
+ {"23455000", 4, [numModes]string{
+ "23450000", "23450000",
+ "23450000", "23460000", "23460000",
+ "23460000", "23460000"}},
+ {"23455001", 4, [numModes]string{
+ "23450000", "23450000",
+ "23460000", "23460000", "23460000",
+ "23460000", "23460000"}},
+ {"23455100", 4, [numModes]string{
+ "23450000", "23450000",
+ "23460000", "23460000", "23460000",
+ "23460000", "23460000"}},
+
+ {"99994999", 4, [numModes]string{
+ "99990000", "99990000",
+ "99990000", "99990000", "99990000",
+ "100000000", "100000000"}},
+ {"99995000", 4, [numModes]string{
+ "99990000", "99990000",
+ "99990000", "100000000", "100000000",
+ "100000000", "100000000"}},
+ {"99999999", 4, [numModes]string{
+ "99990000", "99990000",
+ "100000000", "100000000", "100000000",
+ "100000000", "100000000"}},
+
+ {"12994999", 4, [numModes]string{
+ "12990000", "12990000",
+ "12990000", "12990000", "12990000",
+ "13000000", "13000000"}},
+ {"12995000", 4, [numModes]string{
+ "12990000", "12990000",
+ "12990000", "13000000", "13000000",
+ "13000000", "13000000"}},
+ {"12999999", 4, [numModes]string{
+ "12990000", "12990000",
+ "13000000", "13000000", "13000000",
+ "13000000", "13000000"}},
+ }
+ modes := []RoundingMode{
+ ToZero, ToNegativeInf,
+ ToNearestZero, ToNearestEven, ToNearestAway,
+ AwayFromZero, ToPositiveInf,
+ }
+ for _, tc := range testCases {
+ // Create negative counterpart tests: the sign is reversed and
+ // ToPositiveInf and ToNegativeInf swapped.
+ negModes := tc.modes
+ negModes[1], negModes[6] = negModes[6], negModes[1]
+ for i, res := range negModes {
+ negModes[i] = "-" + res
+ }
+ for i, m := range modes {
+ t.Run(fmt.Sprintf("x:%s/n:%d/%s", tc.x, tc.n, m), func(t *testing.T) {
+ d := mkdec(tc.x)
+ d.round(m, tc.n)
+ if got := d.String(); got != tc.modes[i] {
+ t.Errorf("pos decimal: got %q; want %q", d.String(), tc.modes[i])
+ }
+
+ mult := math.Pow(10, float64(len(tc.x)-tc.n))
+ f := mkfloat(tc.x)
+ f = m.roundFloat(f/mult) * mult
+ if got := fmt.Sprintf("%.0f", f); got != tc.modes[i] {
+ t.Errorf("pos float: got %q; want %q", got, tc.modes[i])
+ }
+
+ // Test the negative case. This is the same as the positive
+ // case, but with ToPositiveInf and ToNegativeInf swapped.
+ d = mkdec(tc.x)
+ d.Neg = true
+ d.round(m, tc.n)
+ if got, want := d.String(), negModes[i]; got != want {
+ t.Errorf("neg decimal: got %q; want %q", d.String(), want)
+ }
+
+ f = -mkfloat(tc.x)
+ f = m.roundFloat(f/mult) * mult
+ if got := fmt.Sprintf("%.0f", f); got != negModes[i] {
+ t.Errorf("neg float: got %q; want %q", got, negModes[i])
+ }
+ })
+ }
+ }
+}
+
+func TestConvert(t *testing.T) {
+ scale2 := RoundingContext{}
+ scale2.SetScale(2)
+ scale2away := RoundingContext{Mode: AwayFromZero}
+ scale2away.SetScale(2)
+ inc0_05 := RoundingContext{Increment: 5, IncrementScale: 2}
+ inc0_05.SetScale(2)
+ inc50 := RoundingContext{Increment: 50}
+ prec3 := RoundingContext{}
+ prec3.SetPrecision(3)
+ roundShift := RoundingContext{DigitShift: 2, MaxFractionDigits: 2}
+ testCases := []struct {
+ x interface{}
+ rc RoundingContext
+ out string
+ }{
+ {-0.001, scale2, "-0.00"},
+ {0.1234, prec3, "0.123"},
+ {1234.0, prec3, "1230"},
+ {1.2345e10, prec3, "12300000000"},
+
+ {int8(-34), scale2, "-34"},
+ {int16(-234), scale2, "-234"},
+ {int32(-234), scale2, "-234"},
+ {int64(-234), scale2, "-234"},
+ {int(-234), scale2, "-234"},
+ {uint8(234), scale2, "234"},
+ {uint16(234), scale2, "234"},
+ {uint32(234), scale2, "234"},
+ {uint64(234), scale2, "234"},
+ {uint(234), scale2, "234"},
+ {-1e9, scale2, "-1000000000.00"},
+ // The following two causes this result to have a lot of digits:
+ // 1) 0.234 cannot be accurately represented as a float64, and
+ // 2) as strconv does not support the rounding AwayFromZero, Convert
+ // leaves the rounding to caller.
+ {0.234, scale2away,
+ "0.2340000000000000135447209004269097931683063507080078125"},
+
+ {0.0249, inc0_05, "0.00"},
+ {0.025, inc0_05, "0.00"},
+ {0.0251, inc0_05, "0.05"},
+ {0.03, inc0_05, "0.05"},
+ {0.049, inc0_05, "0.05"},
+ {0.05, inc0_05, "0.05"},
+ {0.051, inc0_05, "0.05"},
+ {0.0749, inc0_05, "0.05"},
+ {0.075, inc0_05, "0.10"},
+ {0.0751, inc0_05, "0.10"},
+ {324, inc50, "300"},
+ {325, inc50, "300"},
+ {326, inc50, "350"},
+ {349, inc50, "350"},
+ {350, inc50, "350"},
+ {351, inc50, "350"},
+ {374, inc50, "350"},
+ {375, inc50, "400"},
+ {376, inc50, "400"},
+
+ // Here the scale is 2, but the digits get shifted left. As we use
+ // AppendFloat to do the rounding an exta 0 gets added.
+ {0.123, roundShift, "0.1230"},
+
+ {converter(3), scale2, "100"},
+
+ {math.Inf(1), inc50, "Inf"},
+ {math.Inf(-1), inc50, "-Inf"},
+ {math.NaN(), inc50, "NaN"},
+ {"clearly not a number", scale2, "NaN"},
+ }
+ for _, tc := range testCases {
+ var d Decimal
+ t.Run(fmt.Sprintf("%T:%v-%v", tc.x, tc.x, tc.rc), func(t *testing.T) {
+ d.Convert(tc.rc, tc.x)
+ if got := d.String(); got != tc.out {
+ t.Errorf("got %q; want %q", got, tc.out)
+ }
+ })
+ }
+}
+
+type converter int
+
+func (c converter) Convert(d *Decimal, r RoundingContext) {
+ d.Digits = append(d.Digits, 1, 0, 0)
+ d.Exp = 3
+}