diff --git a/scan.go b/scan.go index 69f9a25..3199ef7 100644 --- a/scan.go +++ b/scan.go @@ -268,6 +268,11 @@ Loop: } } + // A lone "." has no digits. The numeric grammar requires at least one. + if len(buf) == 0 { + return ConversionSyntax + } + if big || z.compact == c.Inflated { z.unscaled.SetString(string(buf), 10) z.compact = c.Inflated @@ -300,6 +305,10 @@ func (z *Big) scanExponent(r io.ByteScanner) error { ch, err = r.ReadByte() if err != nil { + if err == io.EOF { + // "1e" has an exponent mark and no digits. + return ConversionSyntax + } return err } var neg bool @@ -318,6 +327,7 @@ func (z *Big) scanExponent(r io.ByteScanner) error { } var exp uint64 + digits := 0 for { ch, err := r.ReadByte() if err != nil { @@ -347,6 +357,12 @@ func (z *Big) scanExponent(r io.ByteScanner) error { return Overflow } exp = v + digits++ + } + + // "1e+" and "1e-" have a sign and no digits. + if digits == 0 { + return ConversionSyntax } if neg { diff --git a/scan_test.go b/scan_test.go index 7d32bb1..a5c06aa 100644 --- a/scan_test.go +++ b/scan_test.go @@ -52,8 +52,6 @@ func BenchmarkBig_SetString(b *testing.B) { globOk = ok } - - func TestUnmarshalTextTwice(t *testing.T) { x := &Big{} @@ -64,6 +62,35 @@ func TestUnmarshalTextTwice(t *testing.T) { } } +func TestSetStringTruncatedNumeric(t *testing.T) { + for _, s := range []string{"1e", "1E", "1e+", "1e-", "-1e", "1.2e", "."} { + x, ok := new(Big).SetString(s) + if !ok { + t.Fatalf("%s: SetString returned false", s) + } + if !x.IsNaN(0) { + t.Fatalf("%s = %s, want NaN", s, x) + } + if x.Context.Conditions&ConversionSyntax == 0 { + t.Fatalf("%s: missing ConversionSyntax", s) + } + } + + kept := []struct{ in, want string }{ + {"1e2", "1E+2"}, + {"1e+2", "1E+2"}, + {"1e-1", "0.1"}, + {"1.", "1"}, + {".5", "0.5"}, + {"10.00", "10.00"}, + } + for _, tc := range kept { + x, ok := new(Big).SetString(tc.in) + if !ok || x.String() != tc.want { + t.Fatalf("%s = %s ok=%v, want %s", tc.in, x, ok, tc.want) + } + } +} func TestSetStringTwice(t *testing.T) { x := &Big{}