diff --git a/decimal.go b/decimal.go index 0dfb880..992126d 100644 --- a/decimal.go +++ b/decimal.go @@ -570,6 +570,16 @@ func (d Decimal) Div(e Decimal) (Decimal, error) { return newDecimal(neg, bintFromBigInt(dBig), defaultPrec), nil } +// MustDiv similar to Div, but panics instead of returning error +func (d Decimal) MustDiv(e Decimal) Decimal { + v, err := d.Div(e) + if err != nil { + panic(err) + } + + return v +} + func tryDivU128(d, e Decimal, neg bool) (Decimal, error) { if d.coef.overflow() || e.coef.overflow() { return Decimal{}, errOverflow @@ -652,6 +662,16 @@ func (d Decimal) QuoRem(e Decimal) (Decimal, Decimal, error) { return q, r, nil } +// MustQuoRem similar to QuoRem, but panics instead of returning error +func (d Decimal) MustQuoRem(e Decimal) (Decimal, Decimal) { + q, r, err := d.QuoRem(e) + if err != nil { + panic(err) + } + + return q, r +} + func tryQuoRemU128(d, e Decimal) (Decimal, Decimal, error) { if d.coef.overflow() || e.coef.overflow() { return Decimal{}, Decimal{}, errOverflow @@ -696,6 +716,16 @@ func (d Decimal) Mod(e Decimal) (Decimal, error) { return r, err } +// MustMod similar to Mod, but panics instead of returning error +func (d Decimal) MustMod(e Decimal) Decimal { + _, r, err := d.QuoRem(e) + if err != nil { + panic(err) + } + + return r +} + // Prec returns decimal precision as an integer func (d Decimal) Prec() int { return int(d.prec) diff --git a/decimal_test.go b/decimal_test.go index 70427ec..963ac29 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -961,6 +961,99 @@ func TestDiv(t *testing.T) { } } +func TestMustDiv(t *testing.T) { + testcases := []struct { + a, b string + overflow bool + wantErr error + }{ + {"22773757910726981402256170801141121114", "811656739243220271.159", false, nil}, + {"22773757910726981402256170801141121024", "2277375793122336353220649475.264577813", false, nil}, + {"2345678901234567899", "1234567890123456789.1234567890123456789", false, nil}, + {"123456.123", "8796093022208", false, nil}, + {"1844674407370955161.5999999999", "18446744073709551616", false, nil}, + {"1000000000000", "0.0000001", false, nil}, + {"479615345916448342049", "1494.186269970473681015", false, nil}, + {"123456.1234567890123456789", "234567.1234567890123456789", false, nil}, + {"123456.1234567890123456789", "1", false, nil}, + {"-123456.1234567890123456789", "234567.1234567890123456789", false, nil}, + {"123456.1234567890123456789", "-234567.1234567890123456789", false, nil}, + {"-123456.1234567890123456789", "-234567.1234567890123456789", false, nil}, + {"9999999999999999999", "1.0001", false, nil}, + {"-9999999999999999999.9999999999999999999", "9999999999999999999", false, nil}, + {"1234567890123456789", "1", false, nil}, + {"1234567890123456789", "2", false, nil}, + {"123456789012345678.9", "0.1", false, nil}, + {"1111111111111", "1111.123456789123456789", false, nil}, + {"123456789", "1.1234567890123456789", false, nil}, + {"0.1234567890123456789", "0.04586201546101", false, nil}, + {"1", "1111.123456789123456789", false, nil}, + {"1", "1.123456789123456789", false, nil}, + {"1", "2", false, nil}, + {"1", "3", false, nil}, + {"1", "4", false, nil}, + {"1", "5", false, nil}, + {"1234567890123456789.1234567890123456879", "1111.1789", false, nil}, + {"123456789123456789.123456789", "3.123456789", false, nil}, + {"123456789123456789.123456789", "3", false, nil}, + {"9999999999999999999", "1234567890123456789.1234567890123456879", false, nil}, + {"9999999999999999999.999999999999999999", "1000000000000000000.1234567890123456789", false, nil}, + {"999999999999999999", "0.100000000000001", false, nil}, + {"123456789123456789.123456789", "0", false, ErrDivideByZero}, + {"1234567890123456789.1234567890123456789", "0.0000000000000000002", true, nil}, + {"1234567890123456789.1234567890123456789", "0.000000001", true, nil}, + {"1000000000000000000000000.1234567890123456789", "-100000000000000000000", true, nil}, + {"1234567890123456789012345678901234567890.1234567890123456789", "1234567890123456789012345678901234567890.1234567890123456789", true, nil}, + {"1234567890123456789012345678901234567890.1234567890123456789", "-1234567890123456789012345678901234567890.1234567890123456789", true, nil}, + {"-1234567890123456789012345678901234567890.1234567890123456789", "1234567890123456789012345678901234567890.1234567890123456789", true, nil}, + {"-1234567890123456789012345678901234567890.1234567890123456789", "-1234567890123456789012345678901234567890.1234567890123456789", true, nil}, + } + + for _, tc := range testcases { + t.Run(tc.a+"/"+tc.b, func(t *testing.T) { + a, err := Parse(tc.a) + require.NoError(t, err) + + b, err := Parse(tc.b) + require.NoError(t, err) + + aStr := a.String() + bStr := b.String() + + if tc.wantErr != nil { + require.PanicsWithError(t, tc.wantErr.Error(), func() { + _ = a.MustDiv(b) + }) + return + } + + c := a.MustDiv(b) + assertOverflow(t, c, tc.overflow) + + // make sure a and b are immutable + require.Equal(t, aStr, a.String()) + require.Equal(t, bStr, b.String()) + + // compare with shopspring/decimal + aa := decimal.RequireFromString(tc.a) + bb := decimal.RequireFromString(tc.b) + + prec := int32(c.Prec()) + cc := aa.DivRound(bb, 28).Truncate(prec) + + // sometimes shopspring/decimal does rounding differently + // e.g. 0.099999999999999 -> 0.1 + // so to check the result, we can check the difference + // between our result and shopspring/decimal result + // valid result should be less than or equal to 1e-19, which is our smallest unit + d := MustParse(cc.String()) + e := c.Sub(d) + + require.LessOrEqual(t, e.Abs().Cmp(oneUnit), 0, "expected %s, got %s", cc.String(), c.String()) + }) + } +} + func TestDivWithCustomPrecision(t *testing.T) { SetDefaultPrecision(14) defer SetDefaultPrecision(maxPrec) @@ -1175,6 +1268,58 @@ func TestQuoRem(t *testing.T) { } } +func TestMustQuoRem(t *testing.T) { + testcases := []struct { + a, b string + q, r Decimal + wantErr error + }{ + {"22773757910726981402256170801141121024", "-20715693594775826464.768", MustParse("-1099348076690522519"), MustParse("3006819284014656913.408"), nil}, + {"12345678901234567890123456.1234567890123456789", "123456789012345678900", MustParse("100000"), MustParse("123456.1234567890123456789"), nil}, + {"12345678901234567890123", "1.1234567890123456789", MustParse("10989010900978142640527"), MustParse("0.4794672386555312197"), nil}, + {"1.1234567890123456789", "123456789012345678900", MustParse("0"), MustParse("1.1234567890123456789"), nil}, + {"12345678901234567890.123456789", "1.1234567890123456789", MustParse("10989010900978142640"), MustParse("0.592997984048161704"), nil}, + {"123456789.1234567890123456789", "123.123456789", MustParse("1002707"), MustParse("37.1369289660123456789"), nil}, + {"1234567890123456789", "1", MustParse("1234567890123456789"), Zero, nil}, + {"11.234", "1.12", MustParse("10"), MustParse("0.034"), nil}, + {"-11.234", "1.12", MustParse("-10"), MustParse("-0.034"), nil}, + {"11.234", "-1.12", MustParse("-10"), MustParse("0.034"), nil}, + {"-11.234", "-1.12", MustParse("10"), MustParse("-0.034"), nil}, + {"123.456", "1.123", MustParse("109"), MustParse("1.049"), nil}, + {"-11.234", "0", MustParse("10"), MustParse("-0.034"), ErrDivideByZero}, + } + + for _, tc := range testcases { + t.Run(fmt.Sprintf("%s.QuoRem(%s)", tc.a, tc.b), func(t *testing.T) { + a, err := Parse(tc.a) + require.NoError(t, err) + + b, err := Parse(tc.b) + require.NoError(t, err) + + if tc.wantErr != nil { + require.PanicsWithError(t, tc.wantErr.Error(), func() { + _, _ = a.MustQuoRem(b) + }) + return + } + + q, r := a.MustQuoRem(b) + + require.Equal(t, tc.q.String(), q.String()) + require.Equal(t, tc.r.String(), r.String()) + + // compare with shopspring/decimal + aa := decimal.RequireFromString(tc.a) + bb := decimal.RequireFromString(tc.b) + + qq, rr := aa.QuoRem(bb, 0) + require.Equal(t, qq.String(), q.String()) + require.Equal(t, rr.String(), r.String()) + }) + } +} + func TestMod(t *testing.T) { testcases := []struct { a, b string @@ -1222,6 +1367,55 @@ func TestMod(t *testing.T) { } } +func TestMustMod(t *testing.T) { + testcases := []struct { + a, b string + r Decimal + wantErr error + }{ + {"12345678901234567890123456.1234567890123456789", "123456789012345678900", MustParse("123456.1234567890123456789"), nil}, + {"12345678901234567890123", "1.1234567890123456789", MustParse("0.4794672386555312197"), nil}, + {"1.1234567890123456789", "123456789012345678900", MustParse("1.1234567890123456789"), nil}, + {"12345678901234567890.123456789", "1.1234567890123456789", MustParse("0.592997984048161704"), nil}, + {"123456789.1234567890123456789", "123.123456789", MustParse("37.1369289660123456789"), nil}, + {"1234567890123456789", "1", Zero, nil}, + {"11.234", "1.12", MustParse("0.034"), nil}, + {"-11.234", "1.12", MustParse("-0.034"), nil}, + {"11.234", "-1.12", MustParse("0.034"), nil}, + {"-11.234", "-1.12", MustParse("-0.034"), nil}, + {"123.456", "1.123", MustParse("1.049"), nil}, + {"-11.234", "0", MustParse("-0.034"), ErrDivideByZero}, + } + + for _, tc := range testcases { + t.Run(fmt.Sprintf("%s.QuoRem(%s)", tc.a, tc.b), func(t *testing.T) { + a, err := Parse(tc.a) + require.NoError(t, err) + + b, err := Parse(tc.b) + require.NoError(t, err) + + if tc.wantErr != nil { + require.PanicsWithError(t, tc.wantErr.Error(), func() { + _ = a.MustMod(b) + }) + return + } + + r := a.MustMod(b) + + require.Equal(t, tc.r.String(), r.String()) + + // compare with shopspring/decimal + aa := decimal.RequireFromString(tc.a) + bb := decimal.RequireFromString(tc.b) + + rr := aa.Mod(bb) + require.Equal(t, rr.String(), r.String()) + }) + } +} + func TestCmp(t *testing.T) { testcases := []struct { a, b string diff --git a/doc_test.go b/doc_test.go index f5c275d..cff8468 100644 --- a/doc_test.go +++ b/doc_test.go @@ -214,6 +214,12 @@ func ExampleDecimal_Div() { // 0 can't divide by zero } +func ExampleDecimal_MustDiv() { + fmt.Println(MustParse("1.23").MustDiv(MustParse("4.12475"))) + // Output: + // 0.2981998909024789381 +} + func ExampleDecimal_Div64() { fmt.Println(MustParse("1.23").Div64(4)) fmt.Println(MustParse("1.23").Div64(0)) @@ -230,6 +236,12 @@ func ExampleDecimal_QuoRem() { // 0 0 can't divide by zero } +func ExampleDecimal_MustQuoRem() { + fmt.Println(MustParse("1.23").MustQuoRem(MustParse("0.5"))) + // Output: + // 2 0.23 +} + func ExampleDecimal_Mod() { fmt.Println(MustParse("1.23").Mod(MustParse("0.5"))) fmt.Println(MustParse("1.23").Mod(MustParse("0"))) @@ -238,6 +250,12 @@ func ExampleDecimal_Mod() { // 0 can't divide by zero } +func ExampleDecimal_MustMod() { + fmt.Println(MustParse("1.23").MustMod(MustParse("0.5"))) + // Output: + // 0.23 +} + func ExampleDecimal_Sub() { a := MustParse("1.23") b := MustParse("4.12475")