package pql import ( "fmt" "math/big" "testing" ) type testAddDecimalCase struct { a Decimal b Decimal exp Decimal } func TestAddDecimal(t *testing.T) { toDecimal := func(a interface{}) Decimal { switch ac := a.(type) { case string: return mustParse(t, ac) case Decimal: return ac default: t.Fatalf("cannot support type %T", ac) } return Decimal{} } newTestAddDecimalCase := func(a, b, exp interface{}) testAddDecimalCase { return testAddDecimalCase{ a: toDecimal(a), b: toDecimal(b), exp: toDecimal(exp), } } tests := []testAddDecimalCase{ newTestAddDecimalCase("40.37", "40.37", "80.74"), newTestAddDecimalCase("18.5", "9.25", "27.75"), newTestAddDecimalCase("18.50", "9.25", "27.75"), newTestAddDecimalCase("-18.50", "-9.25", "-27.75"), newTestAddDecimalCase("-40.37", "40.37", NewDecimal(0, 0)), newTestAddDecimalCase("-50.38", "-50.38", "-100.76"), newTestAddDecimalCase("-40.381", "-40.38", "-80.761"), newTestAddDecimalCase("-40.3700000000000000000001", "-50.38", "-90.7500000000000000000001"), newTestAddDecimalCase("10.37000000000000001", "-10", "0.37000000000000001"), // this is a weird one. because we reduce precision when we parse strings, we // expect the value to be smaller than it really should be if you did // the math yourself. // // there's not really a good way around this unless we use math/big.Int to represent // our Value/Scale. // // but if we did this, to quote seebs: "i suspect that // our performance would tank so badly by the time we were doing >63-bit // numbers that there's no real-world benefit to us." newTestAddDecimalCase("10.370000000000000001", "-10", "0.37"), newTestAddDecimalCase("-9223372036854775807", "-9223372036854775807", "-18446744073709551614"), newTestAddDecimalCase("9223372036854775807", "9223372036854775807", "18446744073709551614"), newTestAddDecimalCase(NewDecimal(0, 0), NewDecimal(1, 4), NewDecimal(1, 4)), } for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { t.Logf("first %#v %#v", test.a, test.b) if got := AddDecimal(test.a, test.b); !got.EqualTo(test.exp) { t.Logf("LOG: %#v + %#v, expected %#v, got %#v", test.a, test.b, test.exp, got) t.Errorf("%v + %v, expected %v, got %v", test.a, test.b, test.exp, got) } t.Logf("second %v %v", test.a, test.b) if got := AddDecimal(test.b, test.a); !got.EqualTo(test.exp) { t.Logf("LOG: %#v + %#v, expected %#v, got %#v", test.a, test.b, test.exp, got) t.Errorf("%v + %v, expected %v, got %v", test.b, test.a, test.exp, got) } }) } } func mustParse(t *testing.T, num string) Decimal { t.Helper() d, err := ParseDecimal(num) if err != nil { v := big.NewInt(0) if _, ok := v.SetString(num, 10); !ok { t.Fatalf("unexpected error parsing %s to Decimal: %v", num, err) } d.value = *v } return d }