From db76742519a9168741302b16d7d1070ce8b4470e Mon Sep 17 00:00:00 2001 From: samricotta Date: Wed, 31 Jul 2024 17:44:03 +0200 Subject: [PATCH] updates on using scientific notation (cherry picked from commit 7ede82f79bab5eb13da8dc02ef80ef90112a521f) --- math/dec.go | 17 ++++++-------- math/dec_test.go | 61 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/math/dec.go b/math/dec.go index 13f72513a6ff..86d5f279e214 100644 --- a/math/dec.go +++ b/math/dec.go @@ -381,19 +381,16 @@ func (x Dec) Marshal() ([]byte, error) { // Unmarshal parses a byte slice containing a text-formatted decimal and stores the result in the receiver. // It returns an error if the byte slice does not represent a valid decimal. func (x *Dec) Unmarshal(data []byte) error { - var d apd.Decimal - _, _, err := d.SetString(string(data)) + result, err := NewDecFromString(string(data)) if err != nil { return ErrInvalidDec.Wrap(err.Error()) } - switch d.Form { - case apd.NaN, apd.NaNSignaling: - return ErrInvalidDec.Wrap("not a number") - case apd.Infinite: - return ErrInvalidDec.Wrap("infinite decimal value not allowed") - default: - x.dec = d - return nil + if result.dec.Form != apd.Finite { + return ErrInvalidDec.Wrap("unknown decimal form") } + + x.dec = result.dec + return nil + } diff --git a/math/dec_test.go b/math/dec_test.go index 6f084bced476..cb53b749e3c1 100644 --- a/math/dec_test.go +++ b/math/dec_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/cockroachdb/apd/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -1325,6 +1326,14 @@ func TestMarshal(t *testing.T) { x: must(NewDecFromString("-1." + strings.Repeat("0", 34))), exp: "-1.0000000000000000000000000000000000", }, + "6 decimal places": { + x: must(NewDecFromString("0.000001")), + exp: "0.000001", + }, + "7 decimal places": { + x: must(NewDecFromString("0.0000001")), + exp: "1E-7", + }, "1e100000": { x: NewDecWithPrec(1, 100_000), exp: "1E+100000", @@ -1353,13 +1362,13 @@ func TestUnMarshal(t *testing.T) { exp string expErr error }{ - "No trailing zeros": { - x: "123456", + "Leading zeros": { + x: "000123456", exp: "123456", }, "Trailing zeros": { - x: "1.23456E+8", - exp: "123456000", + x: "1.00000", + exp: "1.00000", }, "Small e": { x: "1.23456e+8", @@ -1369,6 +1378,10 @@ func TestUnMarshal(t *testing.T) { x: "0", exp: "0", }, + "-0": { + x: "-0", + exp: "-0", + }, "Decimal value": { x: "1.3000", exp: "1.3000", @@ -1381,13 +1394,21 @@ func TestUnMarshal(t *testing.T) { x: "-1E+1", exp: "-10", }, - "max decimal": { - x: "9", - exp: "9", + "Max Exponent": { + x: "1e" + strconv.Itoa(apd.MaxExponent), + exp: "1e" + strconv.Itoa(apd.MaxExponent), }, - "min decimal": { - x: "-1", - exp: "-1", + "Above Max Exponent": { + x: "1e" + strconv.Itoa(apd.MaxExponent+1), + expErr: ErrInvalidDec, + }, + "Min Exponent": { + x: "1e-100000", + exp: "1e-100000", + }, + "Below Min Exponent": { + x: "1e" + strconv.Itoa(apd.MinExponent-1), + expErr: ErrInvalidDec, }, "1e100000": { x: "1E+100000", @@ -1417,6 +1438,18 @@ func TestUnMarshal(t *testing.T) { x: "1foo", expErr: ErrInvalidDec, }, + ".": { + x: ".", + expErr: ErrInvalidDec, + }, + "0.": { + x: "0.", + exp: "0", + }, + ".0": { + x: ".0", + exp: "0.0", + }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { @@ -1426,12 +1459,12 @@ func TestUnMarshal(t *testing.T) { require.ErrorIs(t, err, spec.expErr) return } - if unmarshaled.dec.Exponent == 100000 { - coeffStr := unmarshaled.dec.Coeff.String() + if unmarshaled.dec.Exponent == 100000 || unmarshaled.dec.Exponent == -100000 { if unmarshaled.dec.Negative { - coeffStr = "-" + coeffStr + assert.Equal(t, spec.exp, "-"+unmarshaled.dec.Coeff.String()+"e"+strconv.Itoa(int(unmarshaled.dec.Exponent))) + } else { + assert.Equal(t, spec.exp, unmarshaled.dec.Coeff.String()+"e"+strconv.Itoa(int(unmarshaled.dec.Exponent))) } - assert.Equal(t, spec.exp, coeffStr+"e"+strconv.Itoa(int(unmarshaled.dec.Exponent))) } else { assert.Equal(t, spec.exp, unmarshaled.String()) }