strconv: fix incorrect bit size in ParseComplex; add tests

In ParseComplex, the "size" passed to parseFloatPrefix should be 64 for
complex128, not 128. It still works because of how parseFloatPrefix
is forgiving about the size if it's not 32, but worth fixing anyway.

Make ParseComplex and ParseFloat return a bit size error for anything
other than 128 or 64 (for ParseComplex), or 64 or 32 (for ParseFloat).
Add "InvalidBitSize" tests for these cases.

Add tests for ParseComplex with bitSize==64: this is done in a similar
way to how the ParseFloat 32-bit tests work, re-using the tests for the
larger bit size.

Add tests for FormatComplex -- there were none before.

Fixes #40706

Change-Id: I16ddd546e5237207cc3b8c2181dd708eca42b04f
Reviewed-on: https://go-review.googlesource.com/c/go/+/248219
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Minux Ma <minux@golang.org>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Ben Hoyt 2020-08-14 22:58:49 +12:00 committed by Robert Griesemer
parent 01efc9a3c5
commit 60f42ea61c
6 changed files with 101 additions and 3 deletions

View File

@ -40,10 +40,10 @@ func convErr(err error, s string) (syntax, range_ error) {
// away from the largest floating point number of the given component's size,
// ParseComplex returns err.Err = ErrRange and c = ±Inf for the respective component.
func ParseComplex(s string, bitSize int) (complex128, error) {
size := 128
if bitSize == 64 {
size = 32 // complex64 uses float32 parts
if bitSize != 64 && bitSize != 128 {
return 0, bitSizeError(fnParseComplex, s, bitSize)
}
size := bitSize >> 1
orig := s

View File

@ -198,5 +198,27 @@ func TestParseComplex(t *testing.T) {
if !(cmplx.IsNaN(test.out) && cmplx.IsNaN(got)) && got != test.out {
t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
}
if complex128(complex64(test.out)) == test.out {
got, err := ParseComplex(test.in, 64)
if !reflect.DeepEqual(err, test.err) {
t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
}
got64 := complex64(got)
if complex128(got64) != test.out {
t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
}
}
}
}
func TestParseComplexInvalidBitSize(t *testing.T) {
_, err := ParseComplex("1+2i", 100)
const want = `strconv.ParseComplex: parsing "1+2i": invalid bit size 100`
if err == nil {
t.Fatalf("got nil error, want %q", want)
}
if err.Error() != want {
t.Fatalf("got error %q, want %q", err, want)
}
}

View File

@ -688,6 +688,9 @@ func atof64(s string) (f float64, n int, err error) {
// ParseFloat recognizes the strings "NaN", and the (possibly signed) strings "Inf" and "Infinity"
// as their respective special floating point values. It ignores case when matching.
func ParseFloat(s string, bitSize int) (float64, error) {
if bitSize != 32 && bitSize != 64 {
return 0, bitSizeError(fnParseFloat, s, bitSize)
}
f, n, err := parseFloatPrefix(s, bitSize)
if err == nil && n != len(s) {
return 0, syntaxError(fnParseFloat, s)

View File

@ -634,6 +634,17 @@ func TestRoundTrip32(t *testing.T) {
t.Logf("tested %d float32's", count)
}
func TestParseFloatInvalidBitSize(t *testing.T) {
_, err := ParseFloat("3.14", 100)
const want = `strconv.ParseFloat: parsing "3.14": invalid bit size 100`
if err == nil {
t.Fatalf("got nil error, want %q", want)
}
if err.Error() != want {
t.Fatalf("got error %q, want %q", err, want)
}
}
func BenchmarkAtof64Decimal(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseFloat("33909", 64)

53
src/strconv/ctoa_test.go Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2020 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 strconv_test
import (
. "strconv"
"testing"
)
func TestFormatComplex(t *testing.T) {
tests := []struct {
c complex128
fmt byte
prec int
bitSize int
out string
}{
// a variety of signs
{1 + 2i, 'g', -1, 128, "(1+2i)"},
{3 - 4i, 'g', -1, 128, "(3-4i)"},
{-5 + 6i, 'g', -1, 128, "(-5+6i)"},
{-7 - 8i, 'g', -1, 128, "(-7-8i)"},
// test that fmt and prec are working
{3.14159 + 0.00123i, 'e', 3, 128, "(3.142e+00+1.230e-03i)"},
{3.14159 + 0.00123i, 'f', 3, 128, "(3.142+0.001i)"},
{3.14159 + 0.00123i, 'g', 3, 128, "(3.14+0.00123i)"},
// ensure bitSize rounding is working
{1.2345678901234567 + 9.876543210987654i, 'f', -1, 128, "(1.2345678901234567+9.876543210987654i)"},
{1.2345678901234567 + 9.876543210987654i, 'f', -1, 64, "(1.2345679+9.876543i)"},
// other cases are handled by FormatFloat tests
}
for _, test := range tests {
out := FormatComplex(test.c, test.fmt, test.prec, test.bitSize)
if out != test.out {
t.Fatalf("FormatComplex(%v, %q, %d, %d) = %q; want %q",
test.c, test.fmt, test.prec, test.bitSize, out, test.out)
}
}
}
func TestFormatComplexInvalidBitSize(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatalf("expected panic due to invalid bitSize")
}
}()
_ = FormatComplex(1+2i, 'g', -1, 100)
}

View File

@ -212,6 +212,15 @@ func TestFtoaRandom(t *testing.T) {
}
}
func TestFormatFloatInvalidBitSize(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatalf("expected panic due to invalid bitSize")
}
}()
_ = FormatFloat(3.14, 'g', -1, 100)
}
var ftoaBenches = []struct {
name string
float float64