From 55b257019bccc3afe5a1884cf6857cccf3960dd8 Mon Sep 17 00:00:00 2001 From: pj Date: Mon, 27 Jan 2020 10:21:39 +1100 Subject: [PATCH 01/47] - add ParseComplex func --- src/strconv/atof.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/strconv/atof.go b/src/strconv/atof.go index 23de70b1c9b6cc..1fd11e677d5e77 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -678,3 +678,7 @@ func ParseFloat(s string, bitSize int) (float64, error) { } return atof64(s) } + +func ParseComplex(s string, bitSize int) (float64, error) { + return 0,nil +} From 8afa9a5b852331976079a5e08bb4d6f5a832ad17 Mon Sep 17 00:00:00 2001 From: pj Date: Tue, 28 Jan 2020 09:30:31 +1100 Subject: [PATCH 02/47] - added ParseComplex function --- src/strconv/atoc.go | 155 +++++++++++++++++++++++++++++++++++++++ src/strconv/atoc_test.go | 17 +++++ src/strconv/atof.go | 4 - 3 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 src/strconv/atoc.go create mode 100644 src/strconv/atoc_test.go diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go new file mode 100644 index 00000000000000..dd57888831a71e --- /dev/null +++ b/src/strconv/atoc.go @@ -0,0 +1,155 @@ +// Copyright 2009 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 + +func convErr(err error, s string) error { + if x, ok := err.(*NumError); ok { + x.Func = fnParseComplex + x.Num = s + } + return err +} + +func parseFloat(s, orig string, bitSize int) (float64, error) { + if bitSize == 64 { + f, err := ParseFloat(s, 32) + if err != nil { + return 0, convErr(err, orig) + } + return f, nil + } + + f, err := ParseFloat(s, 64) + if err != nil { + return 0, convErr(err, orig) + } + return f, nil +} + +const fnParseComplex = "ParseComplex" + +// ParseComplex converts the string s to a complex number +// with the precision specified by bitSize: 64 for complex64, or 128 for complex128. +// When bitSize=64, the result still has type complex128, but it will be +// convertible to complex64 without changing its value. +// +// The number represented by s may or may not be parenthesized and have the format (N+Ni) where N is +// a floating-point number. There must not be spaces between the real and imaginary components. +// +// ParseComplex accepts decimal and hexadecimal floating-point number syntax. +// If s is well-formed and near a valid floating-point number, +// ParseComplex returns the nearest floating-point number rounded +// using IEEE754 unbiased rounding. +// (Parsing a hexadecimal floating-point value only rounds when +// there are more bits in the hexadecimal representation than +// will fit in the mantissa.) +// +// The errors that ParseComplex returns have concrete type *NumError +// and include err.Num = s. +// +// If s is not syntactically well-formed, ParseComplex returns err.Err = ErrSyntax. +// +// If s is syntactically well-formed but is more than 1/2 ULP +// away from the largest floating point number of the given size, +// ParseComplex returns f = ±Inf, err.Err = ErrRange. +// +// ParseComplex recognizes the strings "NaN", "+Inf", and "-Inf" as their +// respective special floating point values for each component. It ignores case when matching. +func ParseComplex(s string, bitSize int) (complex128, error) { + + orig := s + + if len(s) == 0 { + return 0, syntaxError(fnParseComplex, orig) + } + + lastChar := s[len(s)-1 : len(s)] + + // Remove brackets + if len(s) > 1 && s[0:1] == "(" && lastChar == ")" { + s = s[1 : len(s)-1] + lastChar = s[len(s)-1 : len(s)] + } + + // Is last character an i? + if lastChar != "i" { + // The last character is not an i so there is only a real component. + real, err := parseFloat(s, orig, bitSize) + if err != nil { + return 0, err + } + return complex(real, 0), nil + } + + // Remove last char which is an i + s = s[0 : len(s)-1] + + // Count how many ± exist. + pos := []int{} + + for idx, rune := range s { + if rune == '+' || rune == '-' { + pos = append(pos, idx) + } + } + + if len(pos) == 0 { + // There is only an imaginary component + + if s == "" { + s = "1" + } + + imag, err := parseFloat(s, orig, bitSize) + if err != nil { + return 0, err + } + return complex(0, imag), nil + + } else if len(pos) > 4 { + // Too many ± exists for a valid complex number + return 0, syntaxError(fnParseComplex, orig) + } + + /* From here onwards, it is either complex number with both a real and imaginary component OR a pure imaginary number in exponential form. */ + + // Loop through pos from middle of slice, outwards + mid := (len(pos) - 1) >> 1 + for j := 0; j < len(pos); j++ { + var idx int + if j%2 == 0 { + idx = mid - j/2 + } else { + idx = mid + (j/2 + 1) + } + + left := s[0:pos[idx]] + right := s[pos[idx]:] + + // Check if left and right are valid float64 + real, err := parseFloat(left, orig, bitSize) + if err != nil { + continue + } + + if right == "+" || right == "-" { + right = right + "1" + } + + imag, err := parseFloat(right, orig, bitSize) + if err != nil { + continue + } + + return complex(real, imag), nil + } + + // Pure imaginary number in exponential form + imag, err := parseFloat(s, orig, bitSize) + if err != nil { + return 0, err + } + return complex(0, imag), nil +} diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go new file mode 100644 index 00000000000000..14d044da01dc37 --- /dev/null +++ b/src/strconv/atoc_test.go @@ -0,0 +1,17 @@ +// Copyright 2009 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 () + +// Test cases required: +// hex form +// exp form +// Only real +// Only imag +// Both real and imag +// With and without parentheses +// NaN +// ±Inf diff --git a/src/strconv/atof.go b/src/strconv/atof.go index 1fd11e677d5e77..23de70b1c9b6cc 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -678,7 +678,3 @@ func ParseFloat(s string, bitSize int) (float64, error) { } return atof64(s) } - -func ParseComplex(s string, bitSize int) (float64, error) { - return 0,nil -} From a16a92702f1e148a38f083294c5f345a1e188029 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 29 Jan 2020 15:44:27 +1100 Subject: [PATCH 03/47] - fix up bug --- src/strconv/atoc.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index dd57888831a71e..cc1d65adb9cf1f 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -4,6 +4,8 @@ package strconv +const fnParseComplex = "ParseComplex" + func convErr(err error, s string) error { if x, ok := err.(*NumError); ok { x.Func = fnParseComplex @@ -28,8 +30,6 @@ func parseFloat(s, orig string, bitSize int) (float64, error) { return f, nil } -const fnParseComplex = "ParseComplex" - // ParseComplex converts the string s to a complex number // with the precision specified by bitSize: 64 for complex64, or 128 for complex128. // When bitSize=64, the result still has type complex128, but it will be @@ -99,7 +99,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // There is only an imaginary component if s == "" { - s = "1" + s = s + "1" } imag, err := parseFloat(s, orig, bitSize) @@ -113,7 +113,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { return 0, syntaxError(fnParseComplex, orig) } - /* From here onwards, it is either complex number with both a real and imaginary component OR a pure imaginary number in exponential form. */ + /* From here onwards, it is either a complex number with both a real and imaginary component OR a pure imaginary number in exponential form. */ // Loop through pos from middle of slice, outwards mid := (len(pos) - 1) >> 1 @@ -128,6 +128,10 @@ func ParseComplex(s string, bitSize int) (complex128, error) { left := s[0:pos[idx]] right := s[pos[idx]:] + if left == "" { + left = left + "0" + } + // Check if left and right are valid float64 real, err := parseFloat(left, orig, bitSize) if err != nil { From 8df2934a6e2bd1f600a09973e83d4bafc12fb99e Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 29 Jan 2020 15:45:08 +1100 Subject: [PATCH 04/47] - added some tests --- src/strconv/atoc_test.go | 112 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 14d044da01dc37..69ad04b3d59216 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -4,7 +4,10 @@ package strconv_test -import () +import ( + . "strconv" + "testing" +) // Test cases required: // hex form @@ -15,3 +18,110 @@ import () // With and without parentheses // NaN // ±Inf + +type tcase struct { + str string + expAnswer complex128 + expErr error +} + +func TestParseComplex(t *testing.T) { + + tests := []tcase{ + { + str: "99", + expAnswer: complex(99, 0), + }, + { + str: "+99", + expAnswer: complex(99, 0), + }, + { + str: "-99", + expAnswer: complex(-99, 0), + }, + { + str: "+1i", + expAnswer: complex(0, 1), + }, + { + str: "-1i", + expAnswer: complex(0, -1), + }, + { + str: "+3-i", + expAnswer: complex(3, -1), + }, + { + str: "+3+i", + expAnswer: complex(3, 1), + }, + { + str: "3-i", + expAnswer: complex(3, -1), + }, + { + str: "3+i", + expAnswer: complex(3, 1), + }, + { + str: "+i", + expAnswer: complex(0, 1), + }, + { + str: "-i", + expAnswer: complex(0, -1), + }, + { + str: "3e3-i", + expAnswer: complex(3e3, -1), + }, + { + str: "-3e3-i", + expAnswer: complex(-3e3, -1), + }, + { + str: "+3e3-i", + expAnswer: complex(3e3, -1), + }, + { + str: "3e+3-i", + expAnswer: complex(3e+3, -1), + }, + { + str: "-3e+3-i", + expAnswer: complex(-3e+3, -1), + }, + { + str: "-3e+3-i", + expAnswer: complex(-3e+3, -1), + }, + { + str: "+3e+3-3e+3i", + expAnswer: complex(3e+3, -3e+3), + }, + { + str: "+3e+3+3e+3i", + expAnswer: complex(3e+3, 3e+3), + }, + } + + for i, tc := range tests { + + got, gotErr := ParseComplex(tc.str, 128) + if gotErr != nil { + if tc.expErr == nil { + t.Errorf("%d: |got: %v |expected: %v", i, gotErr, tc.expErr) + } + } else { + if tc.expErr != nil { + t.Errorf("%d: |got: %v |expected: %v", i, got, tc.expErr) + } else { + if got != tc.expAnswer { + t.Errorf("%d: |got: %v |expected: %v", i, got, tc.expAnswer) + } + } + } + } + +} From bdf80fdb3a2f0d0d9109677d6b3e4eebe24da838 Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 27 Mar 2020 12:11:19 +1100 Subject: [PATCH 05/47] - rename parseFloat to parseComplexComponent --- src/strconv/atoc.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index cc1d65adb9cf1f..b7028ee532b421 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -14,7 +14,7 @@ func convErr(err error, s string) error { return err } -func parseFloat(s, orig string, bitSize int) (float64, error) { +func parseComplexComponent(s, orig string, bitSize int) (float64, error) { if bitSize == 64 { f, err := ParseFloat(s, 32) if err != nil { @@ -76,7 +76,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // Is last character an i? if lastChar != "i" { // The last character is not an i so there is only a real component. - real, err := parseFloat(s, orig, bitSize) + real, err := parseComplexComponent(s, orig, bitSize) if err != nil { return 0, err } @@ -102,7 +102,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { s = s + "1" } - imag, err := parseFloat(s, orig, bitSize) + imag, err := parseComplexComponent(s, orig, bitSize) if err != nil { return 0, err } @@ -133,7 +133,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { } // Check if left and right are valid float64 - real, err := parseFloat(left, orig, bitSize) + real, err := parseComplexComponent(left, orig, bitSize) if err != nil { continue } @@ -142,7 +142,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { right = right + "1" } - imag, err := parseFloat(right, orig, bitSize) + imag, err := parseComplexComponent(right, orig, bitSize) if err != nil { continue } @@ -151,7 +151,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { } // Pure imaginary number in exponential form - imag, err := parseFloat(s, orig, bitSize) + imag, err := parseComplexComponent(s, orig, bitSize) if err != nil { return 0, err } From fd5df020411d9a0c7af694fe317c1a24fc782331 Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 27 Mar 2020 12:34:08 +1100 Subject: [PATCH 06/47] - implement FormatComplex --- src/strconv/ctoa.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/strconv/ctoa.go diff --git a/src/strconv/ctoa.go b/src/strconv/ctoa.go new file mode 100644 index 00000000000000..66d7d808f3c93b --- /dev/null +++ b/src/strconv/ctoa.go @@ -0,0 +1,43 @@ +// Copyright 2009 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 + +// FormatComplex converts the complex number c to a string, +// according to the format fmt and precision prec. It rounds the +// result assuming that the original was obtained from a complex +// value of bitSize bits (128 for complex128, 64 for complex64). +// +// The format fmt is one of +// 'b' (-ddddp±ddd, a binary exponent), +// 'e' (-d.dddde±dd, a decimal exponent), +// 'E' (-d.ddddE±dd, a decimal exponent), +// 'f' (-ddd.dddd, no exponent), +// 'g' ('e' for large exponents, 'f' otherwise), +// 'G' ('E' for large exponents, 'f' otherwise), +// 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or +// 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent). +// +// The precision prec controls the number of digits (excluding the exponent) +// printed by the 'e', 'E', 'f', 'g', 'G', 'x', and 'X' formats. +// For 'e', 'E', 'f', 'x', and 'X', it is the number of digits after the decimal point. +// For 'g' and 'G' it is the maximum number of significant digits (trailing +// zeros are removed). +// The special precision -1 uses the smallest number of digits +// necessary such that ParseComplex will return f exactly. +func FormatComplex(c complex128, fmt byte, prec, bitSize int) string { + + if bitSize == 64 { + bitSize = 32 + } else { + bitSize = 64 + } + + imag := FormatFloat(imag(c), fmt, prec, bitSize) + if imag[0:1] != "+" && imag[0:1] != "-" { + imag = "+" + imag + } + + return "(" + FormatFloat(real(c), fmt, prec, bitSize) + imag + "i)" +} From 0f31e5a37a1934e96e0369638dc5e7c82ea492ba Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 27 Mar 2020 12:34:30 +1100 Subject: [PATCH 07/47] - return j immediately --- src/strconv/atoc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index b7028ee532b421..1d7e3b424f202f 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -99,7 +99,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // There is only an imaginary component if s == "" { - s = s + "1" + return complex(0, 1), nil } imag, err := parseComplexComponent(s, orig, bitSize) From 04ebaf1473a36f6780e15e7adea10cbc1402c736 Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 27 Mar 2020 17:05:56 +1100 Subject: [PATCH 08/47] - simplify code --- src/strconv/atoc.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 1d7e3b424f202f..0876ca3c851542 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -16,14 +16,12 @@ func convErr(err error, s string) error { func parseComplexComponent(s, orig string, bitSize int) (float64, error) { if bitSize == 64 { - f, err := ParseFloat(s, 32) - if err != nil { - return 0, convErr(err, orig) - } - return f, nil + bitSize = 32 + } else { + bitSize = 64 } - f, err := ParseFloat(s, 64) + f, err := ParseFloat(s, bitSize) if err != nil { return 0, convErr(err, orig) } From 07a8ba4cf15376e3fcf516fd3a0123cb007ce828 Mon Sep 17 00:00:00 2001 From: pj Date: Sun, 29 Mar 2020 12:14:56 +1100 Subject: [PATCH 09/47] - update docs --- src/strconv/atoi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strconv/atoi.go b/src/strconv/atoi.go index a4a8a37fb42525..4e3792cef5720f 100644 --- a/src/strconv/atoi.go +++ b/src/strconv/atoi.go @@ -22,7 +22,7 @@ var ErrSyntax = errors.New("invalid syntax") // A NumError records a failed conversion. type NumError struct { - Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat) + Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat, ParseComplex) Num string // the input Err error // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.) } From cefda0cc228774114f4588309a1c71c4452b2565 Mon Sep 17 00:00:00 2001 From: pj Date: Sat, 11 Apr 2020 12:12:09 +1000 Subject: [PATCH 10/47] - update readme --- src/strconv/ctoa.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strconv/ctoa.go b/src/strconv/ctoa.go index 66d7d808f3c93b..aa33234feb32b9 100644 --- a/src/strconv/ctoa.go +++ b/src/strconv/ctoa.go @@ -25,7 +25,7 @@ package strconv // For 'g' and 'G' it is the maximum number of significant digits (trailing // zeros are removed). // The special precision -1 uses the smallest number of digits -// necessary such that ParseComplex will return f exactly. +// necessary such that ParseComplex will return c exactly. func FormatComplex(c complex128, fmt byte, prec, bitSize int) string { if bitSize == 64 { From 4ec856a005f1a9af6f54c3d4cc870c451e89b0d9 Mon Sep 17 00:00:00 2001 From: pj Date: Mon, 20 Apr 2020 22:00:33 +1000 Subject: [PATCH 11/47] - amend based on review comments - User rune instead of string for comparison --- src/strconv/ctoa.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/strconv/ctoa.go b/src/strconv/ctoa.go index aa33234feb32b9..331e48d1aef266 100644 --- a/src/strconv/ctoa.go +++ b/src/strconv/ctoa.go @@ -1,4 +1,4 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// 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. @@ -27,15 +27,15 @@ package strconv // The special precision -1 uses the smallest number of digits // necessary such that ParseComplex will return c exactly. func FormatComplex(c complex128, fmt byte, prec, bitSize int) string { - if bitSize == 64 { bitSize = 32 } else { bitSize = 64 } + // Check if imaginary part has a sign. If not, add one. imag := FormatFloat(imag(c), fmt, prec, bitSize) - if imag[0:1] != "+" && imag[0:1] != "-" { + if imag[0] != '+' && imag[0] != '-' { imag = "+" + imag } From d0e2ade25766de88c29d2c72e5a5c0b3bfb0a15f Mon Sep 17 00:00:00 2001 From: pj Date: Mon, 20 Apr 2020 22:04:15 +1000 Subject: [PATCH 12/47] - amend based on review comments --- src/strconv/atoc.go | 50 +++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 0876ca3c851542..cd6e52c7a8547d 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -1,4 +1,4 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// 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. @@ -33,8 +33,9 @@ func parseComplexComponent(s, orig string, bitSize int) (float64, error) { // When bitSize=64, the result still has type complex128, but it will be // convertible to complex64 without changing its value. // -// The number represented by s may or may not be parenthesized and have the format (N+Ni) where N is -// a floating-point number. There must not be spaces between the real and imaginary components. +// The number represented by s may or may not be parenthesized and have the format +// (N+Ni) where N is a floating-point number. There must not be spaces between the real +// and imaginary components. // // ParseComplex accepts decimal and hexadecimal floating-point number syntax. // If s is well-formed and near a valid floating-point number, @@ -56,23 +57,21 @@ func parseComplexComponent(s, orig string, bitSize int) (float64, error) { // ParseComplex recognizes the strings "NaN", "+Inf", and "-Inf" as their // respective special floating point values for each component. It ignores case when matching. func ParseComplex(s string, bitSize int) (complex128, error) { - - orig := s - if len(s) == 0 { - return 0, syntaxError(fnParseComplex, orig) + return 0, syntaxError(fnParseComplex, s) } + orig := s - lastChar := s[len(s)-1 : len(s)] + endCh := s[len(s)-1] // Remove brackets - if len(s) > 1 && s[0:1] == "(" && lastChar == ")" { + if len(s) > 1 && s[0] == '(' && endCh == ')' { s = s[1 : len(s)-1] - lastChar = s[len(s)-1 : len(s)] + endCh = s[len(s)-1] } // Is last character an i? - if lastChar != "i" { + if endCh != 'i' { // The last character is not an i so there is only a real component. real, err := parseComplexComponent(s, orig, bitSize) if err != nil { @@ -85,17 +84,15 @@ func ParseComplex(s string, bitSize int) (complex128, error) { s = s[0 : len(s)-1] // Count how many ± exist. - pos := []int{} - - for idx, rune := range s { - if rune == '+' || rune == '-' { - pos = append(pos, idx) + signPos := []int{} + for i, ch := range s { + if ch == '+' || ch == '-' { + signPos = append(signPos, i) } } - if len(pos) == 0 { + if len(signPos) == 0 { // There is only an imaginary component - if s == "" { return complex(0, 1), nil } @@ -105,17 +102,17 @@ func ParseComplex(s string, bitSize int) (complex128, error) { return 0, err } return complex(0, imag), nil - - } else if len(pos) > 4 { + } else if len(signPos) > 4 { // Too many ± exists for a valid complex number return 0, syntaxError(fnParseComplex, orig) } - /* From here onwards, it is either a complex number with both a real and imaginary component OR a pure imaginary number in exponential form. */ + // From here onwards, it is either a complex number with both a real and imaginary component + // XOR a pure imaginary number in exponential form. - // Loop through pos from middle of slice, outwards - mid := (len(pos) - 1) >> 1 - for j := 0; j < len(pos); j++ { + // Loop through signPos from middle of slice, outwards + mid := (len(signPos) - 1) >> 1 + for j := 0; j < len(signPos); j++ { var idx int if j%2 == 0 { idx = mid - j/2 @@ -123,8 +120,8 @@ func ParseComplex(s string, bitSize int) (complex128, error) { idx = mid + (j/2 + 1) } - left := s[0:pos[idx]] - right := s[pos[idx]:] + left := s[0:signPos[idx]] + right := s[signPos[idx]:] if left == "" { left = left + "0" @@ -144,7 +141,6 @@ func ParseComplex(s string, bitSize int) (complex128, error) { if err != nil { continue } - return complex(real, imag), nil } From 3e44b92d7b5f1d7ffa13646517c4c668b4c9c626 Mon Sep 17 00:00:00 2001 From: pj Date: Mon, 20 Apr 2020 22:24:03 +1000 Subject: [PATCH 13/47] - amend based on review comments --- src/strconv/atoc_test.go | 115 ++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 62 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 69ad04b3d59216..34eeeaf9d09acb 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -1,4 +1,4 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// 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. @@ -19,109 +19,100 @@ import ( // NaN // ±Inf -type tcase struct { - str string - expAnswer complex128 - expErr error -} - func TestParseComplex(t *testing.T) { - - tests := []tcase{ + tests := []struct { + str string + want complex128 + wantErr error + }{ { - str: "99", - expAnswer: complex(99, 0), + str: "99", + want: complex(99, 0), }, { - str: "+99", - expAnswer: complex(99, 0), + str: "+99", + want: complex(99, 0), }, { - str: "-99", - expAnswer: complex(-99, 0), + str: "-99", + want: complex(-99, 0), }, { - str: "+1i", - expAnswer: complex(0, 1), + str: "+1i", + want: complex(0, 1), }, { - str: "-1i", - expAnswer: complex(0, -1), + str: "-1i", + want: complex(0, -1), }, { - str: "+3-i", - expAnswer: complex(3, -1), + str: "+3-i", + want: complex(3, -1), }, { - str: "+3+i", - expAnswer: complex(3, 1), + str: "+3+i", + want: complex(3, 1), }, { - str: "3-i", - expAnswer: complex(3, -1), + str: "3-i", + want: complex(3, -1), }, { - str: "3+i", - expAnswer: complex(3, 1), + str: "3+i", + want: complex(3, 1), }, { - str: "+i", - expAnswer: complex(0, 1), + str: "+i", + want: complex(0, 1), }, { - str: "-i", - expAnswer: complex(0, -1), + str: "-i", + want: complex(0, -1), }, { - str: "3e3-i", - expAnswer: complex(3e3, -1), + str: "3e3-i", + want: complex(3e3, -1), }, { - str: "-3e3-i", - expAnswer: complex(-3e3, -1), + str: "-3e3-i", + want: complex(-3e3, -1), }, { - str: "+3e3-i", - expAnswer: complex(3e3, -1), + str: "+3e3-i", + want: complex(3e3, -1), }, { - str: "3e+3-i", - expAnswer: complex(3e+3, -1), + str: "3e+3-i", + want: complex(3e+3, -1), }, { - str: "-3e+3-i", - expAnswer: complex(-3e+3, -1), + str: "-3e+3-i", + want: complex(-3e+3, -1), }, { - str: "-3e+3-i", - expAnswer: complex(-3e+3, -1), + str: "-3e+3-i", + want: complex(-3e+3, -1), }, { - str: "+3e+3-3e+3i", - expAnswer: complex(3e+3, -3e+3), + str: "+3e+3-3e+3i", + want: complex(3e+3, -3e+3), }, { - str: "+3e+3+3e+3i", - expAnswer: complex(3e+3, 3e+3), + str: "+3e+3+3e+3i", + want: complex(3e+3, 3e+3), }, } - for i, tc := range tests { - - got, gotErr := ParseComplex(tc.str, 128) - if gotErr != nil { - if tc.expErr == nil { - t.Errorf("%d: |got: %v |expected: %v", i, gotErr, tc.expErr) + for _, tt := range tests { + tt := tt + t.Run(tt.str, func(t *testing.T) { + got, err := ParseComplex(tt.str, 128) + if g, w := err, tt.wantErr; g != w { + t.Fatalf("Error mismatch\nGot: %v\nWant: %v", g, w) } - } else { - if tc.expErr != nil { - t.Errorf("%d: |got: %v |expected: %v", i, got, tc.expErr) - } else { - if got != tc.expAnswer { - t.Errorf("%d: |got: %v |expected: %v", i, got, tc.expAnswer) - } + if got != tt.want { + t.Fatalf("Result mismatch\nGot: %v\nWant: %v", got, tt.want) } - } + }) } - } From 1d98820ece77ecd4e6ee6740c2b7ee786eee5c86 Mon Sep 17 00:00:00 2001 From: pj Date: Mon, 20 Apr 2020 23:02:35 +1000 Subject: [PATCH 14/47] - remove unnecessary left --- src/strconv/atoc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index cd6e52c7a8547d..da564987392754 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -124,7 +124,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { right := s[signPos[idx]:] if left == "" { - left = left + "0" + left = "0" } // Check if left and right are valid float64 From 76ac483a98f6788dd3dcc73f5d0fbe13ed7559cb Mon Sep 17 00:00:00 2001 From: pj Date: Tue, 21 Apr 2020 12:22:02 +1000 Subject: [PATCH 15/47] - update/add/elaborate comments - change var names --- src/strconv/atoc.go | 39 ++++++++++++++++++++++++--------------- src/strconv/ctoa.go | 2 +- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index da564987392754..6b4e912f91913f 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -107,10 +107,22 @@ func ParseComplex(s string, bitSize int) (complex128, error) { return 0, syntaxError(fnParseComplex, orig) } - // From here onwards, it is either a complex number with both a real and imaginary component - // XOR a pure imaginary number in exponential form. - - // Loop through signPos from middle of slice, outwards + // From here onwards, s is either of the forms: + // * Complex number with both a real and imaginary component: N+Ni + // * Purely an imaginary number in exponential form: Ni + // + // More precisely it should look like: + // * ⊞2±10i (len signPos = 1 or 2) + // * ⊞3e±10±3i (len signPos = 2 or 3) [real in exp form] + // * ⊞3e10±5i (len signPos = 1 or 2) [real in exp form] + // * ⊞3e±10±4e±10i (len signPos = 3 or 4) [real and imag in exp form] + // + // where ⊞ means ± or non-existent. + + // Loop through signPos from middle of slice, outwards. + // The idea is if len(signPos) is 3 or 4, then it is more efficient + // to call ParseFloat from the middle, which increases the chance of + // correctly separating the real and imaginary components. mid := (len(signPos) - 1) >> 1 for j := 0; j < len(signPos); j++ { var idx int @@ -120,24 +132,21 @@ func ParseComplex(s string, bitSize int) (complex128, error) { idx = mid + (j/2 + 1) } - left := s[0:signPos[idx]] - right := s[signPos[idx]:] - - if left == "" { - left = "0" + realStr, imagStr := s[0:signPos[idx]], s[signPos[idx]:] + if realStr == "" { + realStr = "0" } - // Check if left and right are valid float64 - real, err := parseComplexComponent(left, orig, bitSize) + // Check if realStr and imagStr are valid float64 + real, err := parseComplexComponent(realStr, orig, bitSize) if err != nil { continue } - if right == "+" || right == "-" { - right = right + "1" + if imagStr == "+" || imagStr == "-" { + imagStr = imagStr + "1" } - - imag, err := parseComplexComponent(right, orig, bitSize) + imag, err := parseComplexComponent(imagStr, orig, bitSize) if err != nil { continue } diff --git a/src/strconv/ctoa.go b/src/strconv/ctoa.go index 331e48d1aef266..a046ae57f69c92 100644 --- a/src/strconv/ctoa.go +++ b/src/strconv/ctoa.go @@ -28,7 +28,7 @@ package strconv // necessary such that ParseComplex will return c exactly. func FormatComplex(c complex128, fmt byte, prec, bitSize int) string { if bitSize == 64 { - bitSize = 32 + bitSize = 32 // complex64 uses float32 internally } else { bitSize = 64 } From 70fa5958d529a74453556d9251dfae1369fc8ed0 Mon Sep 17 00:00:00 2001 From: pj Date: Tue, 21 Apr 2020 13:26:06 +1000 Subject: [PATCH 16/47] - handle NaN case --- src/strconv/atoc.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 6b4e912f91913f..8fe01eb0d2fbfe 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -55,7 +55,8 @@ func parseComplexComponent(s, orig string, bitSize int) (float64, error) { // ParseComplex returns f = ±Inf, err.Err = ErrRange. // // ParseComplex recognizes the strings "NaN", "+Inf", and "-Inf" as their -// respective special floating point values for each component. It ignores case when matching. +// respective special floating point values for each component. "NaN+NaNi" is also +// recognized. It ignores case when matching. func ParseComplex(s string, bitSize int) (complex128, error) { if len(s) == 0 { return 0, syntaxError(fnParseComplex, s) @@ -145,6 +146,8 @@ func ParseComplex(s string, bitSize int) (complex128, error) { if imagStr == "+" || imagStr == "-" { imagStr = imagStr + "1" + } else if imagStr == "+NaN" { + imagStr = "NaN" } imag, err := parseComplexComponent(imagStr, orig, bitSize) if err != nil { From 39c41e6c4d3e7fe0d04448b4d02c2425e5152115 Mon Sep 17 00:00:00 2001 From: pj Date: Tue, 21 Apr 2020 13:28:07 +1000 Subject: [PATCH 17/47] - add all test cases except hexadecimal --- src/strconv/atoc_test.go | 124 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 115 insertions(+), 9 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 34eeeaf9d09acb..448abbee90edb4 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -6,18 +6,14 @@ package strconv_test import ( . "strconv" + "math" + "math/cmplx" + "reflect" "testing" ) // Test cases required: // hex form -// exp form -// Only real -// Only imag -// Both real and imag -// With and without parentheses -// NaN -// ±Inf func TestParseComplex(t *testing.T) { tests := []struct { @@ -101,18 +97,128 @@ func TestParseComplex(t *testing.T) { str: "+3e+3+3e+3i", want: complex(3e+3, 3e+3), }, + { + str: "Infi", + want: complex(0, math.Inf(1)), + }, + { + str: "-Infi", + want: complex(0, math.Inf(-1)), + }, + { + str: "Inf", + want: complex(math.Inf(1), 0), + }, + { + str: "-Inf", + want: complex(math.Inf(-1), 0), + }, + { + str: "-Inf-Infi", + want: complex(math.Inf(-1), math.Inf(-1)), + }, + { + str: "-Inf+Infi", + want: complex(math.Inf(-1), math.Inf(1)), + }, + { + str: "NaN", + want: complex(math.NaN(), 0), + }, + { + str: "NaNi", + want: complex(0, math.NaN()), + }, + { + str: "NaN+NaNi", + want: complex(math.NaN(), math.NaN()), + }, + { + str: "NaN+NaNi", + want: complex(math.NaN(), math.NaN()), + }, + { + str: "NaN+NaNi", + want: complex(math.NaN(), math.NaN()), + }, + // { + // str: "0xBadFace+0x677a2fcc40c6i", + // want: complex(0xBadFace, 0x677a2fcc40c6), + // }, + // { + // str: "0x10.3p-8+0x3p3i", + // want: complex(0x10.3p-8, 0x3p3), + // }, + // { + // str: "+0x10.3p-8+0x3p3i", + // want: complex(+0x10.3p-8, 0x3p3), + // }, + // { + // str: "0x10.3p+8-0x3p3i", + // want: complex(0x10.3p+8, -0x3p3), + // }, + + // Malformed cases + { + str: "30+3i)", + wantErr: ErrSyntax, + }, + { + str: "(30+4i", + wantErr: ErrSyntax, + }, + { + str: "(", + wantErr: ErrSyntax, + }, + { + str: ")", + wantErr: ErrSyntax, + }, + { + str: "foo", + wantErr: ErrSyntax, + }, + { + str: "10e+10+30i+", + wantErr: ErrSyntax, + }, + { + str: "10 + 5i", + wantErr: ErrSyntax, + }, } for _, tt := range tests { tt := tt + if tt.wantErr != nil { + tt.wantErr = &NumError{Func: "ParseComplex", Num: tt.str, Err: tt.wantErr} + } + t.Run(tt.str, func(t *testing.T) { got, err := ParseComplex(tt.str, 128) - if g, w := err, tt.wantErr; g != w { + if g, w := err, tt.wantErr; !reflect.DeepEqual(g, w) { t.Fatalf("Error mismatch\nGot: %v\nWant: %v", g, w) } - if got != tt.want { + + if !(cmplx.IsNaN(tt.want) && cmplx.IsNaN(got)) && got != tt.want { t.Fatalf("Result mismatch\nGot: %v\nWant: %v", got, tt.want) } }) + + // Test with parentheses + if tt.wantErr == nil { + tt.str = "(" + tt.str + ")" + t.Run(tt.str, func(t *testing.T) { + got, err := ParseComplex(tt.str, 128) + if g, w := err, tt.wantErr; g != w { + t.Fatalf("Error mismatch\nGot: %v\nWant: %v", g, w) + } + + if !(cmplx.IsNaN(tt.want) && cmplx.IsNaN(got)) && got != tt.want { + t.Fatalf("Result mismatch\nGot: %v\nWant: %v", got, tt.want) + } + }) + } } } From f86cfad7db0e3c30301e94dda93b15fc75b9e9c0 Mon Sep 17 00:00:00 2001 From: pj Date: Tue, 21 Apr 2020 18:17:51 +1000 Subject: [PATCH 18/47] - fix up tests --- src/strconv/atoc.go | 1 - src/strconv/atoc_test.go | 19 ++++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 8fe01eb0d2fbfe..6b0706771df576 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -117,7 +117,6 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // * ⊞3e±10±3i (len signPos = 2 or 3) [real in exp form] // * ⊞3e10±5i (len signPos = 1 or 2) [real in exp form] // * ⊞3e±10±4e±10i (len signPos = 3 or 4) [real and imag in exp form] - // // where ⊞ means ± or non-existent. // Loop through signPos from middle of slice, outwards. diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 448abbee90edb4..aeac70158d9dce 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -133,14 +133,6 @@ func TestParseComplex(t *testing.T) { str: "NaN+NaNi", want: complex(math.NaN(), math.NaN()), }, - { - str: "NaN+NaNi", - want: complex(math.NaN(), math.NaN()), - }, - { - str: "NaN+NaNi", - want: complex(math.NaN(), math.NaN()), - }, // { // str: "0xBadFace+0x677a2fcc40c6i", // want: complex(0xBadFace, 0x677a2fcc40c6), @@ -208,11 +200,12 @@ func TestParseComplex(t *testing.T) { // Test with parentheses if tt.wantErr == nil { - tt.str = "(" + tt.str + ")" - t.Run(tt.str, func(t *testing.T) { - got, err := ParseComplex(tt.str, 128) - if g, w := err, tt.wantErr; g != w { - t.Fatalf("Error mismatch\nGot: %v\nWant: %v", g, w) + str := "(" + tt.str + ")" + + t.Run(str, func(t *testing.T) { + got, err := ParseComplex(str, 128) + if err != nil { + t.Fatalf("Error mismatch\nGot: %v\nWant: %v", err, nil) } if !(cmplx.IsNaN(tt.want) && cmplx.IsNaN(got)) && got != tt.want { From 0b92e412a9a562fc323e82e807ece1eda81a865d Mon Sep 17 00:00:00 2001 From: pj Date: Tue, 21 Apr 2020 18:45:09 +1000 Subject: [PATCH 19/47] - added hex tests. --- src/strconv/atoc_test.go | 45 +++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index aeac70158d9dce..f713c17cd09c61 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -12,8 +12,16 @@ import ( "testing" ) -// Test cases required: -// hex form +func mustFormatComplex(r, i float64) string { + s1 := FormatFloat(r, 'x', -1, 64) + s2 := FormatFloat(i, 'x', -1, 64) + + if i > 0 { + return s1 + "+" + s2 + "i" + } + + return s1 + s2 + "i" +} func TestParseComplex(t *testing.T) { tests := []struct { @@ -133,23 +141,22 @@ func TestParseComplex(t *testing.T) { str: "NaN+NaNi", want: complex(math.NaN(), math.NaN()), }, - // { - // str: "0xBadFace+0x677a2fcc40c6i", - // want: complex(0xBadFace, 0x677a2fcc40c6), - // }, - // { - // str: "0x10.3p-8+0x3p3i", - // want: complex(0x10.3p-8, 0x3p3), - // }, - // { - // str: "+0x10.3p-8+0x3p3i", - // want: complex(+0x10.3p-8, 0x3p3), - // }, - // { - // str: "0x10.3p+8-0x3p3i", - // want: complex(0x10.3p+8, -0x3p3), - // }, - + { + str: mustFormatComplex(0xBadFace, 0x677a2fcc40c6), + want: complex(0xBadFace, 0x677a2fcc40c6), + }, + { + str: "0x10.3p-8+0x3p3i", + want: complex(0x10.3p-8, 0x3p3), + }, + { + str: "+0x10.3p-8+0x3p3i", + want: complex(0x10.3p-8, 0x3p3), + }, + { + str: "0x10.3p+8-0x3p3i", + want: complex(0x10.3p+8, -0x3p3), + }, // Malformed cases { str: "30+3i)", From 356ebcb0faecc243aced8d456ae9819b5e721921 Mon Sep 17 00:00:00 2001 From: pj Date: Mon, 27 Apr 2020 00:10:00 +1000 Subject: [PATCH 20/47] - add "i" test case --- src/strconv/atoc_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index f713c17cd09c61..ea8e37c90d4e72 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -16,7 +16,7 @@ func mustFormatComplex(r, i float64) string { s1 := FormatFloat(r, 'x', -1, 64) s2 := FormatFloat(i, 'x', -1, 64) - if i > 0 { + if i >= 0 { return s1 + "+" + s2 + "i" } @@ -65,6 +65,10 @@ func TestParseComplex(t *testing.T) { str: "3+i", want: complex(3, 1), }, + { + str: "i", + want: complex(0, 1), + }, { str: "+i", want: complex(0, 1), From a15d62cce447a3e92896ada6b35efec28e574d62 Mon Sep 17 00:00:00 2001 From: pj Date: Mon, 27 Apr 2020 00:16:18 +1000 Subject: [PATCH 21/47] - add extra note --- src/strconv/atoc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 6b0706771df576..865d236ed21779 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -16,7 +16,7 @@ func convErr(err error, s string) error { func parseComplexComponent(s, orig string, bitSize int) (float64, error) { if bitSize == 64 { - bitSize = 32 + bitSize = 32 // complex64 uses float32 internally } else { bitSize = 64 } From 45a837dd0939ec7ff57c7b4bcecd909819884b1f Mon Sep 17 00:00:00 2001 From: pj Date: Tue, 28 Apr 2020 10:16:51 +1000 Subject: [PATCH 22/47] - rearrange some code --- src/strconv/atoc.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 865d236ed21779..ec6f8bda7b4768 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -15,12 +15,6 @@ func convErr(err error, s string) error { } func parseComplexComponent(s, orig string, bitSize int) (float64, error) { - if bitSize == 64 { - bitSize = 32 // complex64 uses float32 internally - } else { - bitSize = 64 - } - f, err := ParseFloat(s, bitSize) if err != nil { return 0, convErr(err, orig) @@ -61,11 +55,19 @@ func ParseComplex(s string, bitSize int) (complex128, error) { if len(s) == 0 { return 0, syntaxError(fnParseComplex, s) } + orig := s + // Translate bitSize for ParseFloat function + if bitSize == 64 { + bitSize = 32 // complex64 uses float32 internally + } else { + bitSize = 64 + } + endCh := s[len(s)-1] - // Remove brackets + // Remove parentheses if len(s) > 1 && s[0] == '(' && endCh == ')' { s = s[1 : len(s)-1] endCh = s[len(s)-1] @@ -132,7 +134,9 @@ func ParseComplex(s string, bitSize int) (complex128, error) { idx = mid + (j/2 + 1) } - realStr, imagStr := s[0:signPos[idx]], s[signPos[idx]:] + pos := signPos[idx] + + realStr, imagStr := s[0:pos], s[pos:] if realStr == "" { realStr = "0" } From 703ee5cb4cf1d00eb15e29a75758c98c2eda9024 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 29 Apr 2020 10:13:59 +1000 Subject: [PATCH 23/47] - add more test cases --- src/strconv/atoc_test.go | 42 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index ea8e37c90d4e72..8f584f329e1973 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -29,6 +29,46 @@ func TestParseComplex(t *testing.T) { want complex128 wantErr error }{ + { + str: "0", + want: complex(0, 0), + }, + { + str: "0i", + want: complex(0, 0), + }, + { + str: "0+0i", + want: complex(0, 0), + }, + { + str: "0.0", + want: complex(0, 0), + }, + { + str: "0.1", + want: complex(0.1, 0), + }, + { + str: "0.0i", + want: complex(0, 0), + }, + { + str: "0.1i", + want: complex(0, 0.1), + }, + { + str: "0.123", + want: complex(0.123, 0), + }, + { + str: "0.123i", + want: complex(0, 0.123), + }, + { + str: "0.123+0.123i", + want: complex(0.123, 0.123), + }, { str: "99", want: complex(99, 0), @@ -193,7 +233,7 @@ func TestParseComplex(t *testing.T) { } for _, tt := range tests { - tt := tt + tt := tt // for capture in Run closures below if tt.wantErr != nil { tt.wantErr = &NumError{Func: "ParseComplex", Num: tt.str, Err: tt.wantErr} } From ad8fbfd170cd138d9757fafea5c13ce846c94ed4 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 29 Apr 2020 10:25:49 +1000 Subject: [PATCH 24/47] - add test to prohibit NaN-NaNi --- src/strconv/atoc_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 8f584f329e1973..f429c7b9656117 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -230,6 +230,10 @@ func TestParseComplex(t *testing.T) { str: "10 + 5i", wantErr: ErrSyntax, }, + { + str: "NaN-NaNi", + wantErr: ErrSyntax, + }, } for _, tt := range tests { From cf61745f4764eaccdbde9ed6ba50157bddc78c97 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 29 Apr 2020 10:36:20 +1000 Subject: [PATCH 25/47] - update docs based on feedback --- src/strconv/ctoa.go | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/strconv/ctoa.go b/src/strconv/ctoa.go index a046ae57f69c92..90d14ed7f7223b 100644 --- a/src/strconv/ctoa.go +++ b/src/strconv/ctoa.go @@ -4,34 +4,18 @@ package strconv -// FormatComplex converts the complex number c to a string, -// according to the format fmt and precision prec. It rounds the -// result assuming that the original was obtained from a complex -// value of bitSize bits (128 for complex128, 64 for complex64). +// FormatComplex converts the complex number c to a string of the +// form (a+bi) where a and b are the real and imaginary parts, +// formatted according to the format fmt and precision prec. // -// The format fmt is one of -// 'b' (-ddddp±ddd, a binary exponent), -// 'e' (-d.dddde±dd, a decimal exponent), -// 'E' (-d.ddddE±dd, a decimal exponent), -// 'f' (-ddd.dddd, no exponent), -// 'g' ('e' for large exponents, 'f' otherwise), -// 'G' ('E' for large exponents, 'f' otherwise), -// 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or -// 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent). -// -// The precision prec controls the number of digits (excluding the exponent) -// printed by the 'e', 'E', 'f', 'g', 'G', 'x', and 'X' formats. -// For 'e', 'E', 'f', 'x', and 'X', it is the number of digits after the decimal point. -// For 'g' and 'G' it is the maximum number of significant digits (trailing -// zeros are removed). -// The special precision -1 uses the smallest number of digits -// necessary such that ParseComplex will return c exactly. +// The format fmt and precision prec have the same meaning as in FormatFloat. +// It rounds the result assuming that the original was obtained from a complex +// value of bitSize bits, which must be 64 for complex64 and 128 for complex128. func FormatComplex(c complex128, fmt byte, prec, bitSize int) string { - if bitSize == 64 { - bitSize = 32 // complex64 uses float32 internally - } else { - bitSize = 64 + if bitSize != 64 && bitSize != 128 { + panic("invalid bitSize") } + bitSize >>= 1 // complex64 uses float32 internally // Check if imaginary part has a sign. If not, add one. imag := FormatFloat(imag(c), fmt, prec, bitSize) From 77e646b33ffe38c55523923c34189a631c08bb73 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 29 Apr 2020 10:46:12 +1000 Subject: [PATCH 26/47] - add skeleton parseFloat func in line with feedback --- src/strconv/atof.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/strconv/atof.go b/src/strconv/atof.go index 23de70b1c9b6cc..9b05fe6640399c 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -647,6 +647,16 @@ func atof64(s string) (f float64, err error) { return f, err } +// TODO: See https://go-review.googlesource.com/c/go/+/216617#message-7befb50c86c0a4ecca842e783a687429eca0d0df +func parseFloat(s string, bitSize int) (float64, int, error) { + if bitSize == 32 { + f, err := atof32(s) + return float64(f), len(s), err + } + f, err := atof64(s) + return f, len(s), err +} + // ParseFloat converts the string s to a floating-point number // with the precision specified by bitSize: 32 for float32, or 64 for float64. // When bitSize=32, the result still has type float64, but it will be @@ -672,9 +682,12 @@ func atof64(s string) (f float64, err error) { // ParseFloat recognizes the strings "NaN", "+Inf", and "-Inf" as their // respective special floating point values. It ignores case when matching. func ParseFloat(s string, bitSize int) (float64, error) { - if bitSize == 32 { - f, err := atof32(s) - return float64(f), err + f, n, err := parseFloat(s, bitSize) + if err != nil { + return 0, err + } + if n != len(s) { + return 0, syntaxError(fnParseFloat, s) } - return atof64(s) + return f, nil } From f3f6211a4f3cf7d207cbd4bfca98b429c7871c43 Mon Sep 17 00:00:00 2001 From: pj Date: Thu, 30 Apr 2020 18:24:52 +1000 Subject: [PATCH 27/47] Revert "- add skeleton parseFloat func in line with feedback" This reverts commit 77e646b33ffe38c55523923c34189a631c08bb73. --- src/strconv/atof.go | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/strconv/atof.go b/src/strconv/atof.go index 9b05fe6640399c..23de70b1c9b6cc 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -647,16 +647,6 @@ func atof64(s string) (f float64, err error) { return f, err } -// TODO: See https://go-review.googlesource.com/c/go/+/216617#message-7befb50c86c0a4ecca842e783a687429eca0d0df -func parseFloat(s string, bitSize int) (float64, int, error) { - if bitSize == 32 { - f, err := atof32(s) - return float64(f), len(s), err - } - f, err := atof64(s) - return f, len(s), err -} - // ParseFloat converts the string s to a floating-point number // with the precision specified by bitSize: 32 for float32, or 64 for float64. // When bitSize=32, the result still has type float64, but it will be @@ -682,12 +672,9 @@ func parseFloat(s string, bitSize int) (float64, int, error) { // ParseFloat recognizes the strings "NaN", "+Inf", and "-Inf" as their // respective special floating point values. It ignores case when matching. func ParseFloat(s string, bitSize int) (float64, error) { - f, n, err := parseFloat(s, bitSize) - if err != nil { - return 0, err - } - if n != len(s) { - return 0, syntaxError(fnParseFloat, s) + if bitSize == 32 { + f, err := atof32(s) + return float64(f), err } - return f, nil + return atof64(s) } From 3e41873114d816d55f270e00eef2e84061374992 Mon Sep 17 00:00:00 2001 From: pj Date: Thu, 30 Apr 2020 20:19:50 +1000 Subject: [PATCH 28/47] - add more test cases due to how ParseFloatPrefix works --- src/strconv/atoc_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index f429c7b9656117..019256a615cb88 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -202,6 +202,14 @@ func TestParseComplex(t *testing.T) { want: complex(0x10.3p+8, -0x3p3), }, // Malformed cases + { + str: "", + wantErr: ErrSyntax, + }, + { + str: " ", + wantErr: ErrSyntax, + }, { str: "30+3i)", wantErr: ErrSyntax, @@ -230,10 +238,34 @@ func TestParseComplex(t *testing.T) { str: "10 + 5i", wantErr: ErrSyntax, }, + { + str: "+NaN", + wantErr: ErrSyntax, + }, + { + str: "-NaN", + wantErr: ErrSyntax, + }, + { + str: "+NaNi", + wantErr: ErrSyntax, + }, + { + str: "-NaNi", + wantErr: ErrSyntax, + }, { str: "NaN-NaNi", wantErr: ErrSyntax, }, + { + str: "3+3+5.5", + wantErr: ErrSyntax, + }, + { + str: "3+3+5.5i", + wantErr: ErrSyntax, + }, } for _, tt := range tests { From 578f86671b3255ebee92d11604c07e63bc54ab7b Mon Sep 17 00:00:00 2001 From: pj Date: Thu, 30 Apr 2020 20:27:22 +1000 Subject: [PATCH 29/47] - ParseComplex rewritten with new parseFloatPrefix function. --- src/strconv/atoc.go | 120 +++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 74 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index ec6f8bda7b4768..c9ceef15c76678 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -14,14 +14,6 @@ func convErr(err error, s string) error { return err } -func parseComplexComponent(s, orig string, bitSize int) (float64, error) { - f, err := ParseFloat(s, bitSize) - if err != nil { - return 0, convErr(err, orig) - } - return f, nil -} - // ParseComplex converts the string s to a complex number // with the precision specified by bitSize: 64 for complex64, or 128 for complex128. // When bitSize=64, the result still has type complex128, but it will be @@ -76,93 +68,73 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // Is last character an i? if endCh != 'i' { // The last character is not an i so there is only a real component. - real, err := parseComplexComponent(s, orig, bitSize) + real, err := ParseFloat(s, bitSize) if err != nil { - return 0, err + return 0, convErr(err, orig) } return complex(real, 0), nil + } else if s == "i" { + return complex(0, 1), nil } // Remove last char which is an i s = s[0 : len(s)-1] - // Count how many ± exist. - signPos := []int{} - for i, ch := range s { - if ch == '+' || ch == '-' { - signPos = append(signPos, i) - } - } + // Some input does not get interpreted by parseFloatPrefix correctly. + // Namely: i, -i, +i, +NaNi + // The "i" (no sign) case is taken care of above. + // +NaNi is only acceptable if both a real and imag component exist. - if len(signPos) == 0 { - // There is only an imaginary component - if s == "" { - return complex(0, 1), nil - } + var posNaNFound bool - imag, err := parseComplexComponent(s, orig, bitSize) - if err != nil { - return 0, err + if len(s) >= 1 { + endCh := s[len(s)-1] + if endCh == '+' || endCh == '-' { + s = s[0:len(s)] + "1" } - return complex(0, imag), nil - } else if len(signPos) > 4 { - // Too many ± exists for a valid complex number - return 0, syntaxError(fnParseComplex, orig) } - // From here onwards, s is either of the forms: - // * Complex number with both a real and imaginary component: N+Ni - // * Purely an imaginary number in exponential form: Ni - // - // More precisely it should look like: - // * ⊞2±10i (len signPos = 1 or 2) - // * ⊞3e±10±3i (len signPos = 2 or 3) [real in exp form] - // * ⊞3e10±5i (len signPos = 1 or 2) [real in exp form] - // * ⊞3e±10±4e±10i (len signPos = 3 or 4) [real and imag in exp form] - // where ⊞ means ± or non-existent. - - // Loop through signPos from middle of slice, outwards. - // The idea is if len(signPos) is 3 or 4, then it is more efficient - // to call ParseFloat from the middle, which increases the chance of - // correctly separating the real and imaginary components. - mid := (len(signPos) - 1) >> 1 - for j := 0; j < len(signPos); j++ { - var idx int - if j%2 == 0 { - idx = mid - j/2 - } else { - idx = mid + (j/2 + 1) + if len(s) >= 4 { + endChs := s[len(s)-4 : len(s)] + if endChs == "+NaN" { + posNaNFound = true + s = s[0:len(s)-4] + "NaN" // remove sign before NaN } + } - pos := signPos[idx] - - realStr, imagStr := s[0:pos], s[pos:] - if realStr == "" { - realStr = "0" - } + floatsFound := []float64{} - // Check if realStr and imagStr are valid float64 - real, err := parseComplexComponent(realStr, orig, bitSize) + for { + f, n, err := parseFloatPrefix(s, 64) if err != nil { - continue + return 0, convErr(err, orig) } - if imagStr == "+" || imagStr == "-" { - imagStr = imagStr + "1" - } else if imagStr == "+NaN" { - imagStr = "NaN" - } - imag, err := parseComplexComponent(imagStr, orig, bitSize) - if err != nil { - continue + floatsFound = append(floatsFound, f) + s = s[n:] + + if len(s) == 0 { + break } - return complex(real, imag), nil } - // Pure imaginary number in exponential form - imag, err := parseComplexComponent(s, orig, bitSize) - if err != nil { - return 0, err + // Check how many floats were found in s + switch len(floatsFound) { + case 0: + return 0, syntaxError(fnParseComplex, orig) + case 1: + // only imag component + imaj := floatsFound[0] + if posNaNFound && imaj != imaj { + // Reject if +NaN found + return 0, syntaxError(fnParseComplex, orig) + } + return complex(0, floatsFound[0]), nil + case 2: + // real and imag component + return complex(floatsFound[0], floatsFound[1]), nil } - return complex(0, imag), nil + + // Too many components + return 0, syntaxError(fnParseComplex, orig) } From 7a9466b25394140571e303d68d750be688cd5a59 Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 1 May 2020 01:11:22 +1000 Subject: [PATCH 30/47] - finishing touches --- src/strconv/atoc.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index c9ceef15c76678..5af4cd6a6feaa9 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -50,7 +50,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { orig := s - // Translate bitSize for ParseFloat function + // Translate bitSize for ParseFloat/parseFloatPrefix function if bitSize == 64 { bitSize = 32 // complex64 uses float32 internally } else { @@ -90,7 +90,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { if len(s) >= 1 { endCh := s[len(s)-1] if endCh == '+' || endCh == '-' { - s = s[0:len(s)] + "1" + s = s + "1" } } @@ -105,7 +105,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { floatsFound := []float64{} for { - f, n, err := parseFloatPrefix(s, 64) + f, n, err := parseFloatPrefix(s, bitSize) if err != nil { return 0, convErr(err, orig) } @@ -120,8 +120,6 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // Check how many floats were found in s switch len(floatsFound) { - case 0: - return 0, syntaxError(fnParseComplex, orig) case 1: // only imag component imaj := floatsFound[0] @@ -135,6 +133,6 @@ func ParseComplex(s string, bitSize int) (complex128, error) { return complex(floatsFound[0], floatsFound[1]), nil } - // Too many components + // 0 floats found or too many components return 0, syntaxError(fnParseComplex, orig) } From 75c830448504e58785c96cf44806eb1170115f6d Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 1 May 2020 12:55:26 +1000 Subject: [PATCH 31/47] - don't shadow imag builtin func --- src/strconv/ctoa.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/strconv/ctoa.go b/src/strconv/ctoa.go index 90d14ed7f7223b..c16a2e579cf147 100644 --- a/src/strconv/ctoa.go +++ b/src/strconv/ctoa.go @@ -18,10 +18,10 @@ func FormatComplex(c complex128, fmt byte, prec, bitSize int) string { bitSize >>= 1 // complex64 uses float32 internally // Check if imaginary part has a sign. If not, add one. - imag := FormatFloat(imag(c), fmt, prec, bitSize) - if imag[0] != '+' && imag[0] != '-' { - imag = "+" + imag + im := FormatFloat(imag(c), fmt, prec, bitSize) + if im[0] != '+' && im[0] != '-' { + im = "+" + im } - return "(" + FormatFloat(real(c), fmt, prec, bitSize) + imag + "i)" + return "(" + FormatFloat(real(c), fmt, prec, bitSize) + im + "i)" } From 3351300bec3a594dce13e7e10a57ed5de0227b59 Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 1 May 2020 13:07:31 +1000 Subject: [PATCH 32/47] - condense test syntax --- src/strconv/atoc_test.go | 296 ++++++++------------------------------- 1 file changed, 59 insertions(+), 237 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 019256a615cb88..0e8d80b88778d4 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -29,243 +29,65 @@ func TestParseComplex(t *testing.T) { want complex128 wantErr error }{ - { - str: "0", - want: complex(0, 0), - }, - { - str: "0i", - want: complex(0, 0), - }, - { - str: "0+0i", - want: complex(0, 0), - }, - { - str: "0.0", - want: complex(0, 0), - }, - { - str: "0.1", - want: complex(0.1, 0), - }, - { - str: "0.0i", - want: complex(0, 0), - }, - { - str: "0.1i", - want: complex(0, 0.1), - }, - { - str: "0.123", - want: complex(0.123, 0), - }, - { - str: "0.123i", - want: complex(0, 0.123), - }, - { - str: "0.123+0.123i", - want: complex(0.123, 0.123), - }, - { - str: "99", - want: complex(99, 0), - }, - { - str: "+99", - want: complex(99, 0), - }, - { - str: "-99", - want: complex(-99, 0), - }, - { - str: "+1i", - want: complex(0, 1), - }, - { - str: "-1i", - want: complex(0, -1), - }, - { - str: "+3-i", - want: complex(3, -1), - }, - { - str: "+3+i", - want: complex(3, 1), - }, - { - str: "3-i", - want: complex(3, -1), - }, - { - str: "3+i", - want: complex(3, 1), - }, - { - str: "i", - want: complex(0, 1), - }, - { - str: "+i", - want: complex(0, 1), - }, - { - str: "-i", - want: complex(0, -1), - }, - { - str: "3e3-i", - want: complex(3e3, -1), - }, - { - str: "-3e3-i", - want: complex(-3e3, -1), - }, - { - str: "+3e3-i", - want: complex(3e3, -1), - }, - { - str: "3e+3-i", - want: complex(3e+3, -1), - }, - { - str: "-3e+3-i", - want: complex(-3e+3, -1), - }, - { - str: "-3e+3-i", - want: complex(-3e+3, -1), - }, - { - str: "+3e+3-3e+3i", - want: complex(3e+3, -3e+3), - }, - { - str: "+3e+3+3e+3i", - want: complex(3e+3, 3e+3), - }, - { - str: "Infi", - want: complex(0, math.Inf(1)), - }, - { - str: "-Infi", - want: complex(0, math.Inf(-1)), - }, - { - str: "Inf", - want: complex(math.Inf(1), 0), - }, - { - str: "-Inf", - want: complex(math.Inf(-1), 0), - }, - { - str: "-Inf-Infi", - want: complex(math.Inf(-1), math.Inf(-1)), - }, - { - str: "-Inf+Infi", - want: complex(math.Inf(-1), math.Inf(1)), - }, - { - str: "NaN", - want: complex(math.NaN(), 0), - }, - { - str: "NaNi", - want: complex(0, math.NaN()), - }, - { - str: "NaN+NaNi", - want: complex(math.NaN(), math.NaN()), - }, - { - str: mustFormatComplex(0xBadFace, 0x677a2fcc40c6), - want: complex(0xBadFace, 0x677a2fcc40c6), - }, - { - str: "0x10.3p-8+0x3p3i", - want: complex(0x10.3p-8, 0x3p3), - }, - { - str: "+0x10.3p-8+0x3p3i", - want: complex(0x10.3p-8, 0x3p3), - }, - { - str: "0x10.3p+8-0x3p3i", - want: complex(0x10.3p+8, -0x3p3), - }, - // Malformed cases - { - str: "", - wantErr: ErrSyntax, - }, - { - str: " ", - wantErr: ErrSyntax, - }, - { - str: "30+3i)", - wantErr: ErrSyntax, - }, - { - str: "(30+4i", - wantErr: ErrSyntax, - }, - { - str: "(", - wantErr: ErrSyntax, - }, - { - str: ")", - wantErr: ErrSyntax, - }, - { - str: "foo", - wantErr: ErrSyntax, - }, - { - str: "10e+10+30i+", - wantErr: ErrSyntax, - }, - { - str: "10 + 5i", - wantErr: ErrSyntax, - }, - { - str: "+NaN", - wantErr: ErrSyntax, - }, - { - str: "-NaN", - wantErr: ErrSyntax, - }, - { - str: "+NaNi", - wantErr: ErrSyntax, - }, - { - str: "-NaNi", - wantErr: ErrSyntax, - }, - { - str: "NaN-NaNi", - wantErr: ErrSyntax, - }, - { - str: "3+3+5.5", - wantErr: ErrSyntax, - }, - { - str: "3+3+5.5i", - wantErr: ErrSyntax, - }, + {"0", complex(0, 0), nil}, + {"0i", complex(0, 0), nil}, + {"0+0i", complex(0, 0), nil}, + {"0.0", complex(0, 0), nil}, + {"0.1", complex(0.1, 0), nil}, + {"0.0i", complex(0, 0), nil}, + {"0.1i", complex(0, 0.1), nil}, + {"0.123", complex(0.123, 0), nil}, + {"0.123i", complex(0, 0.123), nil}, + {"0.123+0.123i", complex(0.123, 0.123), nil}, + {"99", complex(99, 0), nil}, + {"+99", complex(99, 0), nil}, + {"-99", complex(-99, 0), nil}, + {"+1i", complex(0, 1), nil}, + {"-1i", complex(0, -1), nil}, + {"+3-i", complex(3, -1), nil}, + {"+3+i", complex(3, 1), nil}, + {"3-i", complex(3, -1), nil}, + {"3+i", complex(3, 1), nil}, + {"i", complex(0, 1), nil}, + {"+i", complex(0, 1), nil}, + {"-i", complex(0, -1), nil}, + {"3e3-i", complex(3e3, -1), nil}, + {"-3e3-i", complex(-3e3, -1), nil}, + {"+3e3-i", complex(3e3, -1), nil}, + {"3e+3-i", complex(3e+3, -1), nil}, + {"-3e+3-i", complex(-3e+3, -1), nil}, + {"-3e+3-i", complex(-3e+3, -1), nil}, + {"+3e+3-3e+3i", complex(3e+3, -3e+3), nil}, + {"+3e+3+3e+3i", complex(3e+3, 3e+3), nil}, + {"Infi", complex(0, math.Inf(1)), nil}, + {"-Infi", complex(0, math.Inf(-1)), nil}, + {"Inf", complex(math.Inf(1), 0), nil}, + {"-Inf", complex(math.Inf(-1), 0), nil}, + {"-Inf-Infi", complex(math.Inf(-1), math.Inf(-1)), nil}, + {"-Inf+Infi", complex(math.Inf(-1), math.Inf(1)), nil}, + {"NaN", complex(math.NaN(), 0), nil}, + {"NaNi", complex(0, math.NaN()), nil}, + {"NaN+NaNi", complex(math.NaN(), math.NaN()), nil}, + {mustFormatComplex(0xBadFace, 0x677a2fcc40c6), complex(0xBadFace, 0x677a2fcc40c6), nil}, + {"0x10.3p-8+0x3p3i",complex(0x10.3p-8, 0x3p3), nil}, + {"+0x10.3p-8+0x3p3i",complex(0x10.3p-8, 0x3p3), nil}, + {"0x10.3p+8-0x3p3i",complex(0x10.3p+8, -0x3p3), nil}, + {"", 0, ErrSyntax}, + {" ", 0, ErrSyntax}, + {"30+3i)", 0, ErrSyntax}, + {"(30+4i", 0, ErrSyntax}, + {"(", 0, ErrSyntax}, + {")", 0, ErrSyntax}, + {"foo", 0, ErrSyntax}, + {"10e+10+30i+", 0, ErrSyntax}, + {"10 + 5i", 0, ErrSyntax}, + {"+NaN", 0, ErrSyntax}, + {"-NaN", 0, ErrSyntax}, + {"+NaNi", 0, ErrSyntax}, + {"-NaNi", 0, ErrSyntax}, + {"NaN-NaNi", 0, ErrSyntax}, + {"3+3+5.5", 0, ErrSyntax}, + {"3+3+5.5i", 0, ErrSyntax}, } for _, tt := range tests { From a9f662b6f0337123869dee32bb43b0fbe6b0161f Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 1 May 2020 13:18:34 +1000 Subject: [PATCH 33/47] - change algorithm --- src/strconv/atoc.go | 113 +++++++++++++++----------------------------- 1 file changed, 38 insertions(+), 75 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 5af4cd6a6feaa9..1a1a04b6b6fa7c 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -44,95 +44,58 @@ func convErr(err error, s string) error { // respective special floating point values for each component. "NaN+NaNi" is also // recognized. It ignores case when matching. func ParseComplex(s string, bitSize int) (complex128, error) { - if len(s) == 0 { - return 0, syntaxError(fnParseComplex, s) - } - - orig := s - - // Translate bitSize for ParseFloat/parseFloatPrefix function + size := 128 if bitSize == 64 { - bitSize = 32 // complex64 uses float32 internally - } else { - bitSize = 64 + size = 32 // complex64 uses float32 parts } - endCh := s[len(s)-1] + orig := s - // Remove parentheses - if len(s) > 1 && s[0] == '(' && endCh == ')' { + // Remove parentheses, if any. + if len(s) >= 2 && s[0] == '(' && s[len(s)-1] == ')' { s = s[1 : len(s)-1] - endCh = s[len(s)-1] } - // Is last character an i? - if endCh != 'i' { - // The last character is not an i so there is only a real component. - real, err := ParseFloat(s, bitSize) - if err != nil { - return 0, convErr(err, orig) - } - return complex(real, 0), nil - } else if s == "i" { - return complex(0, 1), nil + // Read real part (possibly imaginary part if followed by 'i'). + re, n, err := parseFloatPrefix(s, size) + if err != nil { + return 0, convErr(err, orig) } + s = s[n:] - // Remove last char which is an i - s = s[0 : len(s)-1] - - // Some input does not get interpreted by parseFloatPrefix correctly. - // Namely: i, -i, +i, +NaNi - // The "i" (no sign) case is taken care of above. - // +NaNi is only acceptable if both a real and imag component exist. - - var posNaNFound bool - - if len(s) >= 1 { - endCh := s[len(s)-1] - if endCh == '+' || endCh == '-' { - s = s + "1" - } - } - - if len(s) >= 4 { - endChs := s[len(s)-4 : len(s)] - if endChs == "+NaN" { - posNaNFound = true - s = s[0:len(s)-4] + "NaN" // remove sign before NaN - } + // If we have nothing left, we're done. + if len(s) == 0 { + return complex(re, 0), nil } - floatsFound := []float64{} - - for { - f, n, err := parseFloatPrefix(s, bitSize) - if err != nil { - return 0, convErr(err, orig) + // Otherwise, look at the next character. + switch s[0] { + case '+': + // Consume the '+' to avoid an error if we have "+NaNi", but + // do this only if we don't have a "++" (don't hide that error). + if len(s) > 1 && s[1] != '+' { + s = s[1:] } - - floatsFound = append(floatsFound, f) - s = s[n:] - - if len(s) == 0 { - break + case '-': + // ok + case 'i': + // If 'i' is the last character, we only have an imaginary part. + if len(s) == 1 { + return complex(0, re), nil } + fallthrough + default: + return 0, syntaxError(fnParseComplex, orig) } - // Check how many floats were found in s - switch len(floatsFound) { - case 1: - // only imag component - imaj := floatsFound[0] - if posNaNFound && imaj != imaj { - // Reject if +NaN found - return 0, syntaxError(fnParseComplex, orig) - } - return complex(0, floatsFound[0]), nil - case 2: - // real and imag component - return complex(floatsFound[0], floatsFound[1]), nil + // Read imaginary part. + im, n, err := parseFloatPrefix(s, size) + if err != nil { + return 0, convErr(err, orig) } - - // 0 floats found or too many components - return 0, syntaxError(fnParseComplex, orig) + s = s[n:] + if s != "i" { + return 0, syntaxError(fnParseComplex, orig) + } + return complex(re, im), nil } From c561916f184aa453b9b3a4e47575115df856c80f Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 1 May 2020 15:11:08 +1000 Subject: [PATCH 34/47] - add more test cases --- src/strconv/atoc_test.go | 237 ++++++++++++++++++++++++++++++--------- 1 file changed, 183 insertions(+), 54 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 0e8d80b88778d4..abe92a9bbf35db 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -12,29 +12,116 @@ import ( "testing" ) -func mustFormatComplex(r, i float64) string { - s1 := FormatFloat(r, 'x', -1, 64) - s2 := FormatFloat(i, 'x', -1, 64) - - if i >= 0 { - return s1 + "+" + s2 + "i" - } - - return s1 + s2 + "i" -} - func TestParseComplex(t *testing.T) { tests := []struct { str string want complex128 wantErr error }{ - {"0", complex(0, 0), nil}, - {"0i", complex(0, 0), nil}, - {"0+0i", complex(0, 0), nil}, - {"0.0", complex(0, 0), nil}, + // Clear Invalids + {"", 0, ErrSyntax}, + {" ", 0, ErrSyntax}, + {"(", 0, ErrSyntax}, + {")", 0, ErrSyntax}, + {"foo", 0, ErrSyntax}, + {"10 + 5i", 0, ErrSyntax}, + {"3+3+5.5", 0, ErrSyntax}, + {"3+3+5.5i", 0, ErrSyntax}, + + // NaNs + {"NaN", complex(math.NaN(), 0), nil}, + {"NaNi", complex(0, math.NaN()), nil}, + {"NaN+NaNi", complex(math.NaN(), math.NaN()), nil}, + {"NaN++NaNi", 0, ErrSyntax}, + {"+NaN", 0, ErrSyntax}, + {"++NaN", 0, ErrSyntax}, + {"-NaN", 0, ErrSyntax}, + {"+NaNi", 0, ErrSyntax}, + {"-NaNi", 0, ErrSyntax}, + {"NaN-NaNi", 0, ErrSyntax}, + + // Infs + {"Infi", complex(0, math.Inf(1)), nil}, + {"infi", complex(0, math.Inf(1)), nil}, + {"inf i", 0, ErrSyntax}, + {"-Infi", complex(0, math.Inf(-1)), nil}, + {"-infi", complex(0, math.Inf(-1)), nil}, + {"-inf i", 0, ErrSyntax}, + {"Inf", complex(math.Inf(1), 0), nil}, + {"-Inf", complex(math.Inf(-1), 0), nil}, + {"-Inf-Infi", complex(math.Inf(-1), math.Inf(-1)), nil}, + {"-Inf+Infi", complex(math.Inf(-1), math.Inf(1)), nil}, + {"-Inf-Inf i", 0, ErrSyntax}, + {"-Inf+Inf i", 0, ErrSyntax}, + {"-Inf- Inf i", 0, ErrSyntax}, + {"-Inf+ Inf i", 0, ErrSyntax}, + {"-Inf- Infi", 0, ErrSyntax}, + {"-Inf+ Infi", 0, ErrSyntax}, + + // Zeros + {"0", 0, nil}, + {"0i", 0, nil}, + {"0+0i", 0, nil}, + {"0.0", 0, nil}, + {"0.0i", 0, nil}, + {"0.0+0.0i", 0, nil}, + {"0.0-0.0i", 0, nil}, + {"-0.0-0.0i", 0, nil}, + {"-0.0+0.0i", 0, nil}, + {"0e0", 0, nil}, + {"-0e0", 0, nil}, + {"+0e0", 0, nil}, + {"0e-0", 0, nil}, + {"-0e-0", 0, nil}, + {"+0e-0", 0, nil}, + {"0e+0", 0, nil}, + {"-0e+0", 0, nil}, + {"+0e+0", 0, nil}, + {"0e+01234567890123456789", 0, nil}, + {"0.00e-01234567890123456789", 0, nil}, + {"-0e+01234567890123456789", 0, nil}, + {"-0.00e-01234567890123456789", 0, nil}, + {"0x0p+01234567890123456789", 0, nil}, + {"0x0.00p-01234567890123456789", 0, nil}, + {"-0x0p+01234567890123456789", 0, nil}, + {"-0x0.00p-01234567890123456789", 0, nil}, + {"0e0i", 0, nil}, + {"-0e0i", 0, nil}, + {"+0e0i", 0, nil}, + {"0e-0i", 0, nil}, + {"-0e-0i", 0, nil}, + {"+0e-0i", 0, nil}, + {"0e+0i", 0, nil}, + {"-0e+0i", 0, nil}, + {"+0e+0i", 0, nil}, + {"0e+01234567890123456789i", 0, nil}, + {"0.00e-01234567890123456789i", 0, nil}, + {"-0e+01234567890123456789i", 0, nil}, + {"-0.00e-01234567890123456789i", 0, nil}, + {"0x0p+01234567890123456789i", 0, nil}, + {"0x0.00p-01234567890123456789i", 0, nil}, + {"-0x0p+01234567890123456789i", 0, nil}, + {"-0x0.00p-01234567890123456789i", 0, nil}, + {"0+0i", 0, nil}, + {"0e0+0e0i", 0, nil}, + {"-0e0-0e0i", 0, nil}, + {"+0e0+0e0i", 0, nil}, + {"0e-0+0e-0i", 0, nil}, + {"-0e-0-0e-0i", 0, nil}, + {"+0e-0+0e-0i", 0, nil}, + {"0e+0+0e+0i", 0, nil}, + {"-0e+0-0e+0i", 0, nil}, + {"+0e+0+0e+0i", 0, nil}, + {"0e+01234567890123456789+0e+01234567890123456789i", 0, nil}, + {"0.00e-01234567890123456789+0.00e-01234567890123456789i", 0, nil}, + {"-0e+01234567890123456789-0e+01234567890123456789i", 0, nil}, + {"-0.00e-01234567890123456789-0.00e-01234567890123456789i", 0, nil}, + {"0x0p+01234567890123456789+0x0p+01234567890123456789i", 0, nil}, + {"0x0.00p-01234567890123456789+0x0.00p-01234567890123456789i", 0, nil}, + {"-0x0p+01234567890123456789-0x0p+01234567890123456789i", 0, nil}, + {"-0x0.00p-01234567890123456789-0x0.00p-01234567890123456789i", 0, nil}, + {"0.1", complex(0.1, 0), nil}, - {"0.0i", complex(0, 0), nil}, {"0.1i", complex(0, 0.1), nil}, {"0.123", complex(0.123, 0), nil}, {"0.123i", complex(0, 0.123), nil}, @@ -43,51 +130,82 @@ func TestParseComplex(t *testing.T) { {"+99", complex(99, 0), nil}, {"-99", complex(-99, 0), nil}, {"+1i", complex(0, 1), nil}, + {"i", 0, ErrSyntax}, + {"+i", 0, ErrSyntax}, {"-1i", complex(0, -1), nil}, - {"+3-i", complex(3, -1), nil}, - {"+3+i", complex(3, 1), nil}, - {"3-i", complex(3, -1), nil}, - {"3+i", complex(3, 1), nil}, - {"i", complex(0, 1), nil}, - {"+i", complex(0, 1), nil}, - {"-i", complex(0, -1), nil}, - {"3e3-i", complex(3e3, -1), nil}, - {"-3e3-i", complex(-3e3, -1), nil}, - {"+3e3-i", complex(3e3, -1), nil}, - {"3e+3-i", complex(3e+3, -1), nil}, - {"-3e+3-i", complex(-3e+3, -1), nil}, - {"-3e+3-i", complex(-3e+3, -1), nil}, + {"-i", 0, ErrSyntax}, + {"+3-1i", complex(3, -1), nil}, + {"+3-i", 0, ErrSyntax}, + {"+3+1i", complex(3, 1), nil}, + {"+3+i", 0, ErrSyntax}, + {"3-i", 0, ErrSyntax}, + {"3+i", 0, ErrSyntax}, + {"30+3i", complex(30, 3), nil}, + {"30+3i)", 0, ErrSyntax}, + {"(30+4i", 0, ErrSyntax}, + {"3e3-1i", complex(3e3, -1), nil}, + {"3e3-i", 0, ErrSyntax}, + {"-3e3-1i", complex(-3e3, -1), nil}, + {"-3e3-i", 0, ErrSyntax}, + {"+3e3-1i", complex(3e3, -1), nil}, + {"+3e3-i", 0, ErrSyntax}, + {"3e+3-1i", complex(3e+3, -1), nil}, + {"3e+3-i", 0, ErrSyntax}, + {"-3e+3-1i", complex(-3e+3, -1), nil}, + {"-3e+3-i", 0, ErrSyntax}, + {"-3e+3-i", 0, ErrSyntax}, {"+3e+3-3e+3i", complex(3e+3, -3e+3), nil}, {"+3e+3+3e+3i", complex(3e+3, 3e+3), nil}, - {"Infi", complex(0, math.Inf(1)), nil}, - {"-Infi", complex(0, math.Inf(-1)), nil}, - {"Inf", complex(math.Inf(1), 0), nil}, - {"-Inf", complex(math.Inf(-1), 0), nil}, - {"-Inf-Infi", complex(math.Inf(-1), math.Inf(-1)), nil}, - {"-Inf+Infi", complex(math.Inf(-1), math.Inf(1)), nil}, - {"NaN", complex(math.NaN(), 0), nil}, - {"NaNi", complex(0, math.NaN()), nil}, - {"NaN+NaNi", complex(math.NaN(), math.NaN()), nil}, + {"+3e+3+3e+3i+", 0, ErrSyntax}, + + // Hexadecimals {mustFormatComplex(0xBadFace, 0x677a2fcc40c6), complex(0xBadFace, 0x677a2fcc40c6), nil}, {"0x10.3p-8+0x3p3i",complex(0x10.3p-8, 0x3p3), nil}, {"+0x10.3p-8+0x3p3i",complex(0x10.3p-8, 0x3p3), nil}, {"0x10.3p+8-0x3p3i",complex(0x10.3p+8, -0x3p3), nil}, - {"", 0, ErrSyntax}, - {" ", 0, ErrSyntax}, - {"30+3i)", 0, ErrSyntax}, - {"(30+4i", 0, ErrSyntax}, - {"(", 0, ErrSyntax}, - {")", 0, ErrSyntax}, - {"foo", 0, ErrSyntax}, - {"10e+10+30i+", 0, ErrSyntax}, - {"10 + 5i", 0, ErrSyntax}, - {"+NaN", 0, ErrSyntax}, - {"-NaN", 0, ErrSyntax}, - {"+NaNi", 0, ErrSyntax}, - {"-NaNi", 0, ErrSyntax}, - {"NaN-NaNi", 0, ErrSyntax}, - {"3+3+5.5", 0, ErrSyntax}, - {"3+3+5.5i", 0, ErrSyntax}, + {"0x1p0", complex(1, 0), nil}, + {"0x1p1", complex(2, 0), nil}, + {"0x1p-1", complex(0.5, 0), nil}, + {"0x1ep-1", complex(15, 0), nil}, + {"-0x1ep-1", complex(-15, 0), nil}, + {"-0x1_ep-1", complex(-15, 0), nil}, + {"0x1p-200", complex(6.223015277861142e-61, 0), nil}, + {"0x1p200", complex(1.6069380442589903e+60, 0), nil}, + {"0x1fFe2.p0", complex(131042, 0), nil}, + {"0x1fFe2.P0", complex(131042, 0), nil}, + {"-0x2p3", complex(-16, 0), nil}, + {"0x0.fp4", complex(15, 0), nil}, + {"0x0.fp0", complex(0.9375, 0), nil}, + {"0x1e2", 0, ErrSyntax}, + {"1p2", 0, ErrSyntax}, + {"0x1p0i", complex(0, 1), nil}, + {"0x1p1i", complex(0, 2), nil}, + {"0x1p-1i", complex(0, 0.5), nil}, + {"0x1ep-1i", complex(0, 15), nil}, + {"-0x1ep-1i", complex(0, -15), nil}, + {"-0x1_ep-1i", complex(0, -15), nil}, + {"0x1p-200i", complex(0, 6.223015277861142e-61), nil}, + {"0x1p200i", complex(0, 1.6069380442589903e+60), nil}, + {"0x1fFe2.p0i", complex(0, 131042), nil}, + {"0x1fFe2.P0i", complex(0, 131042), nil}, + {"-0x2p3i", complex(0, -16), nil}, + {"0x0.fp4i", complex(0, 15), nil}, + {"0x0.fp0i", complex(0, 0.9375), nil}, + {"0x1e2i", 0, ErrSyntax}, + {"1p2i", 0, ErrSyntax}, + {"0x1p0+0x1p0i", complex(1, 1), nil}, + {"0x1p1+0x1p1i", complex(2, 2), nil}, + {"0x1p-1+0x1p-1i", complex(0.5, 0.5), nil}, + {"0x1ep-1+0x1ep-1i", complex(15, 15), nil}, + {"-0x1ep-1-0x1ep-1i", complex(-15, -15), nil}, + {"-0x1_ep-1-0x1_ep-1i", complex(-15, -15), nil}, + {"0x1p-200+0x1p-200i", complex(6.223015277861142e-61, 6.223015277861142e-61), nil}, + {"0x1p200+0x1p200i", complex(1.6069380442589903e+60, 1.6069380442589903e+60), nil}, + {"0x1fFe2.p0+0x1fFe2.p0i", complex(131042, 131042), nil}, + {"0x1fFe2.P0+0x1fFe2.P0i", complex(131042, 131042), nil}, + {"-0x2p3-0x2p3i", complex(-16, -16), nil}, + {"0x0.fp4+0x0.fp4i", complex(15, 15), nil}, + {"0x0.fp0+0x0.fp0i", complex(0.9375, 0.9375), nil}, } for _, tt := range tests { @@ -124,3 +242,14 @@ func TestParseComplex(t *testing.T) { } } } + +func mustFormatComplex(r, i float64) string { + s1 := FormatFloat(r, 'x', -1, 64) + s2 := FormatFloat(i, 'x', -1, 64) + + if i >= 0 { + return s1 + "+" + s2 + "i" + } + + return s1 + s2 + "i" +} From 76942b97371c1dfd41aff0e26cd532e2277f8646 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 6 May 2020 02:43:11 +1000 Subject: [PATCH 35/47] - update documentation --- src/strconv/atoc.go | 27 +++++++++------------------ src/strconv/atof.go | 5 +++-- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 1a1a04b6b6fa7c..542076176cee6a 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -19,30 +19,21 @@ func convErr(err error, s string) error { // When bitSize=64, the result still has type complex128, but it will be // convertible to complex64 without changing its value. // -// The number represented by s may or may not be parenthesized and have the format -// (N+Ni) where N is a floating-point number. There must not be spaces between the real -// and imaginary components. -// -// ParseComplex accepts decimal and hexadecimal floating-point number syntax. -// If s is well-formed and near a valid floating-point number, -// ParseComplex returns the nearest floating-point number rounded -// using IEEE754 unbiased rounding. -// (Parsing a hexadecimal floating-point value only rounds when -// there are more bits in the hexadecimal representation than -// will fit in the mantissa.) +// The number represented by s must be of the form N, Ni, or N±Ni, where N stands +// for a floating-point number as recognized by ParseFloat, and i is the imaginary +// component. If the second N is unsigned, a + sign is required between the two components +// as indicated by the ±. If the second N is NaN, only a + sign is accepted. +// The form may be parenthesized and cannot contain any spaces. +// The resulting complex number consists of the two components converted by ParseFloat. // // The errors that ParseComplex returns have concrete type *NumError // and include err.Num = s. // // If s is not syntactically well-formed, ParseComplex returns err.Err = ErrSyntax. // -// If s is syntactically well-formed but is more than 1/2 ULP -// away from the largest floating point number of the given size, -// ParseComplex returns f = ±Inf, err.Err = ErrRange. -// -// ParseComplex recognizes the strings "NaN", "+Inf", and "-Inf" as their -// respective special floating point values for each component. "NaN+NaNi" is also -// recognized. It ignores case when matching. +// If s is syntactically well-formed but either component is more than 1/2 ULP +// 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 { diff --git a/src/strconv/atof.go b/src/strconv/atof.go index 23de70b1c9b6cc..0de48bd2d9246a 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -669,8 +669,9 @@ func atof64(s string) (f float64, err error) { // away from the largest floating point number of the given size, // ParseFloat returns f = ±Inf, err.Err = ErrRange. // -// ParseFloat recognizes the strings "NaN", "+Inf", and "-Inf" as their -// respective special floating point values. It ignores case when matching. +// ParseFloat recognizes the strings "NaN", "Infinity", "+Infinity", "-Infinity", +// "Inf", +Inf", and "-Inf" as their respective special floating point values. +// It ignores case when matching. func ParseFloat(s string, bitSize int) (float64, error) { if bitSize == 32 { f, err := atof32(s) From da29b0ebc63666206a769799158e97041246b04b Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 6 May 2020 02:44:32 +1000 Subject: [PATCH 36/47] - add ErrRange test cases --- src/strconv/atoc_test.go | 248 +++++++++++++++++++++------------------ 1 file changed, 135 insertions(+), 113 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index abe92a9bbf35db..22a22b01e00ae9 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -12,12 +12,14 @@ import ( "testing" ) +type atocTest struct { + in string + out complex128 + err error +} + func TestParseComplex(t *testing.T) { - tests := []struct { - str string - want complex128 - wantErr error - }{ + tests := []atocTest{ // Clear Invalids {"", 0, ErrSyntax}, {" ", 0, ErrSyntax}, @@ -28,6 +30,10 @@ func TestParseComplex(t *testing.T) { {"3+3+5.5", 0, ErrSyntax}, {"3+3+5.5i", 0, ErrSyntax}, + // Parentheses + {"(3.0+5.5i)", 3.0 + 5.5i, nil}, + {"(3.0+5.5i", 0, ErrSyntax}, + // NaNs {"NaN", complex(math.NaN(), 0), nil}, {"NaNi", complex(0, math.NaN()), nil}, @@ -121,135 +127,151 @@ func TestParseComplex(t *testing.T) { {"-0x0p+01234567890123456789-0x0p+01234567890123456789i", 0, nil}, {"-0x0.00p-01234567890123456789-0x0.00p-01234567890123456789i", 0, nil}, - {"0.1", complex(0.1, 0), nil}, - {"0.1i", complex(0, 0.1), nil}, - {"0.123", complex(0.123, 0), nil}, - {"0.123i", complex(0, 0.123), nil}, - {"0.123+0.123i", complex(0.123, 0.123), nil}, - {"99", complex(99, 0), nil}, - {"+99", complex(99, 0), nil}, - {"-99", complex(-99, 0), nil}, - {"+1i", complex(0, 1), nil}, + {"0.1", 0.1, nil}, + {"0.1i", 0 + 0.1i, nil}, + {"0.123", 0.123, nil}, + {"0.123i", 0 + 0.123i, nil}, + {"0.123+0.123i", 0.123 + 0.123i, nil}, + {"99", 99, nil}, + {"+99", 99, nil}, + {"-99", -99, nil}, + {"+1i", 1i, nil}, {"i", 0, ErrSyntax}, {"+i", 0, ErrSyntax}, - {"-1i", complex(0, -1), nil}, + {"-1i", -1i, nil}, {"-i", 0, ErrSyntax}, - {"+3-1i", complex(3, -1), nil}, - {"+3-i", 0, ErrSyntax}, - {"+3+1i", complex(3, 1), nil}, - {"+3+i", 0, ErrSyntax}, - {"3-i", 0, ErrSyntax}, - {"3+i", 0, ErrSyntax}, - {"30+3i", complex(30, 3), nil}, + {"+3+1i", 3 + 1i, nil}, + {"30+3i", 30 + 3i, nil}, {"30+3i)", 0, ErrSyntax}, {"(30+4i", 0, ErrSyntax}, - {"3e3-1i", complex(3e3, -1), nil}, - {"3e3-i", 0, ErrSyntax}, - {"-3e3-1i", complex(-3e3, -1), nil}, - {"-3e3-i", 0, ErrSyntax}, - {"+3e3-1i", complex(3e3, -1), nil}, - {"+3e3-i", 0, ErrSyntax}, - {"3e+3-1i", complex(3e+3, -1), nil}, - {"3e+3-i", 0, ErrSyntax}, - {"-3e+3-1i", complex(-3e+3, -1), nil}, - {"-3e+3-i", 0, ErrSyntax}, - {"-3e+3-i", 0, ErrSyntax}, - {"+3e+3-3e+3i", complex(3e+3, -3e+3), nil}, - {"+3e+3+3e+3i", complex(3e+3, 3e+3), nil}, + {"+3e+3-3e+3i", 3e+3 - 3e+3i, nil}, + {"+3e+3+3e+3i", 3e+3 + 3e+3i, nil}, {"+3e+3+3e+3i+", 0, ErrSyntax}, // Hexadecimals - {mustFormatComplex(0xBadFace, 0x677a2fcc40c6), complex(0xBadFace, 0x677a2fcc40c6), nil}, - {"0x10.3p-8+0x3p3i",complex(0x10.3p-8, 0x3p3), nil}, - {"+0x10.3p-8+0x3p3i",complex(0x10.3p-8, 0x3p3), nil}, - {"0x10.3p+8-0x3p3i",complex(0x10.3p+8, -0x3p3), nil}, - {"0x1p0", complex(1, 0), nil}, - {"0x1p1", complex(2, 0), nil}, - {"0x1p-1", complex(0.5, 0), nil}, - {"0x1ep-1", complex(15, 0), nil}, - {"-0x1ep-1", complex(-15, 0), nil}, - {"-0x1_ep-1", complex(-15, 0), nil}, - {"0x1p-200", complex(6.223015277861142e-61, 0), nil}, - {"0x1p200", complex(1.6069380442589903e+60, 0), nil}, - {"0x1fFe2.p0", complex(131042, 0), nil}, - {"0x1fFe2.P0", complex(131042, 0), nil}, - {"-0x2p3", complex(-16, 0), nil}, - {"0x0.fp4", complex(15, 0), nil}, - {"0x0.fp0", complex(0.9375, 0), nil}, + {"0x10.3p-8+0x3p3i",0x10.3p-8+0x3p3i, nil}, + {"+0x10.3p-8+0x3p3i",0x10.3p-8+0x3p3i, nil}, + {"0x10.3p+8-0x3p3i",0x10.3p+8-0x3p3i, nil}, + {"0x1p0", 1, nil}, + {"0x1p1", 2, nil}, + {"0x1p-1", 0.5, nil}, + {"0x1ep-1", 15, nil}, + {"-0x1ep-1", -15, nil}, + {"-0x1_ep-1", -15, nil}, + {"0x1p-200", 6.223015277861142e-61, nil}, + {"0x1p200", 1.6069380442589903e+60, nil}, + {"0x1fFe2.p0", 131042, nil}, + {"0x1fFe2.P0", 131042, nil}, + {"-0x2p3", -16, nil}, + {"0x0.fp4", 15, nil}, + {"0x0.fp0", 0.9375, nil}, {"0x1e2", 0, ErrSyntax}, {"1p2", 0, ErrSyntax}, - {"0x1p0i", complex(0, 1), nil}, - {"0x1p1i", complex(0, 2), nil}, - {"0x1p-1i", complex(0, 0.5), nil}, - {"0x1ep-1i", complex(0, 15), nil}, - {"-0x1ep-1i", complex(0, -15), nil}, - {"-0x1_ep-1i", complex(0, -15), nil}, - {"0x1p-200i", complex(0, 6.223015277861142e-61), nil}, - {"0x1p200i", complex(0, 1.6069380442589903e+60), nil}, - {"0x1fFe2.p0i", complex(0, 131042), nil}, - {"0x1fFe2.P0i", complex(0, 131042), nil}, - {"-0x2p3i", complex(0, -16), nil}, - {"0x0.fp4i", complex(0, 15), nil}, - {"0x0.fp0i", complex(0, 0.9375), nil}, + {"0x1p0i", 1i, nil}, + {"0x1p1i", 2i, nil}, + {"0x1p-1i", 0.5i, nil}, + {"0x1ep-1i", 15i, nil}, + {"-0x1ep-1i", -15i, nil}, + {"-0x1_ep-1i", -15i, nil}, + {"0x1p-200i", 6.223015277861142e-61i, nil}, + {"0x1p200i", 1.6069380442589903e+60i, nil}, + {"0x1fFe2.p0i", 131042i, nil}, + {"0x1fFe2.P0i", 131042i, nil}, + {"-0x2p3i", -16i, nil}, + {"0x0.fp4i", 15i, nil}, + {"0x0.fp0i", 0.9375i, nil}, {"0x1e2i", 0, ErrSyntax}, {"1p2i", 0, ErrSyntax}, - {"0x1p0+0x1p0i", complex(1, 1), nil}, - {"0x1p1+0x1p1i", complex(2, 2), nil}, - {"0x1p-1+0x1p-1i", complex(0.5, 0.5), nil}, - {"0x1ep-1+0x1ep-1i", complex(15, 15), nil}, - {"-0x1ep-1-0x1ep-1i", complex(-15, -15), nil}, - {"-0x1_ep-1-0x1_ep-1i", complex(-15, -15), nil}, - {"0x1p-200+0x1p-200i", complex(6.223015277861142e-61, 6.223015277861142e-61), nil}, - {"0x1p200+0x1p200i", complex(1.6069380442589903e+60, 1.6069380442589903e+60), nil}, - {"0x1fFe2.p0+0x1fFe2.p0i", complex(131042, 131042), nil}, - {"0x1fFe2.P0+0x1fFe2.P0i", complex(131042, 131042), nil}, - {"-0x2p3-0x2p3i", complex(-16, -16), nil}, - {"0x0.fp4+0x0.fp4i", complex(15, 15), nil}, - {"0x0.fp0+0x0.fp0i", complex(0.9375, 0.9375), nil}, + {"0x1p0+0x1p0i", 1 + 1i, nil}, + {"0x1p1+0x1p1i", 2 + 2i, nil}, + {"0x1p-1+0x1p-1i", 0.5 + 0.5i, nil}, + {"0x1ep-1+0x1ep-1i", 15 + 15i, nil}, + {"-0x1ep-1-0x1ep-1i", -15 - 15i, nil}, + {"-0x1_ep-1-0x1_ep-1i", -15 - 15i, nil}, + {"0x1p-200+0x1p-200i", 6.223015277861142e-61 + 6.223015277861142e-61i, nil}, + {"0x1p200+0x1p200i", 1.6069380442589903e+60 + 1.6069380442589903e+60i, nil}, + {"0x1fFe2.p0+0x1fFe2.p0i", 131042 + 131042i, nil}, + {"0x1fFe2.P0+0x1fFe2.P0i", 131042 + 131042i, nil}, + {"-0x2p3-0x2p3i", -16 - 16i, nil}, + {"0x0.fp4+0x0.fp4i", 15 + 15i, nil}, + {"0x0.fp0+0x0.fp0i", 0.9375 + 0.9375i, nil}, + + // ErrRange + + // next float64 - too large + {"1.7976931348623159e308+1.7976931348623159e308i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-1.7976931348623159e308-1.7976931348623159e308i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"0x1p1024+0x1p1024i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-0x1p1024-0x1p1024i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"0x2p1023+0x2p1023i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-0x2p1023-0x2p1023i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"0x.1p1028+0x.1p1028i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-0x.1p1028-0x.1p1028i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"0x.2p1027+0x.2p1027i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-0x.2p1027-0x.2p1027i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + + // the border is ...158079 + // borderline - okay + {"1.7976931348623158e308+1.7976931348623158e308i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil}, + {"-1.7976931348623158e308-1.7976931348623158e308i", -1.7976931348623157e+308 - 1.7976931348623157e+308i, nil}, + {"0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil}, + {"-0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 - 1.7976931348623157e+308i, nil}, + // borderline - too large + {"1.797693134862315808e308+1.797693134862315808e308i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-1.797693134862315808e308-1.797693134862315808e308i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"0x1.fffffffffffff8p1023+0x1.fffffffffffff8p1023i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-0x1.fffffffffffff8p1023-0x1.fffffffffffff8p1023i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"0x1fffffffffffff.8p+971+0x1fffffffffffff.8p+971i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-0x1fffffffffffff8p+967-0x1fffffffffffff8p+967i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"0x.1fffffffffffff8p1027+0x.1fffffffffffff8p1027i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-0x.1fffffffffffff9p1027-0x.1fffffffffffff9p1027i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + + // a little too large + {"1e308+1e308i", 1e+308 + 1e+308i, nil}, + {"2e308", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"1e309", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"0x1p1025", complex(math.Inf(1), math.Inf(1)), ErrRange}, + + // way too large + {"1e310+1e310i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-1e310-1e310i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"1e400+1e400i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-1e400-1e400i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"1e400000+1e400000i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-1e400000-1e400000i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"0x1p1030+0x1p1030i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"0x1p2000+0x1p2000i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"0x1p2000000000+0x1p2000000000i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"-0x1p1030-0x1p1030i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"-0x1p2000-0x1p2000i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"-0x1p2000000000-0x1p2000000000i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + + // try to overflow exponent + {"1e-4294967296+1e-4294967296i", 0, nil}, + {"1e+4294967296+1e+4294967296i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"1e-18446744073709551616+1e-18446744073709551616i", 0, nil}, + {"1e+18446744073709551616+1e+18446744073709551616i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"0x1p-4294967296+0x1p-4294967296i", 0, nil}, + {"0x1p+4294967296+0x1p+4294967296i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"0x1p-18446744073709551616+0x1p-18446744073709551616i", 0, nil}, + {"0x1p+18446744073709551616+0x1p+18446744073709551616i", complex(math.Inf(1), math.Inf(1)), ErrRange}, } for _, tt := range tests { tt := tt // for capture in Run closures below - if tt.wantErr != nil { - tt.wantErr = &NumError{Func: "ParseComplex", Num: tt.str, Err: tt.wantErr} + if tt.err != nil { + tt.err = &NumError{Func: "ParseComplex", Num: tt.in, Err: tt.err} } - t.Run(tt.str, func(t *testing.T) { - got, err := ParseComplex(tt.str, 128) - if g, w := err, tt.wantErr; !reflect.DeepEqual(g, w) { - t.Fatalf("Error mismatch\nGot: %v\nWant: %v", g, w) + t.Run(tt.in, func(t *testing.T) { + got, err := ParseComplex(tt.in, 128) + if g, w := err, tt.err; !reflect.DeepEqual(g, w) { + t.Errorf("ParseComplex(%q, 128) = %v, %v want %v %v", tt.in, got, err, tt.out, tt.err) } - if !(cmplx.IsNaN(tt.want) && cmplx.IsNaN(got)) && got != tt.want { - t.Fatalf("Result mismatch\nGot: %v\nWant: %v", got, tt.want) + if !(cmplx.IsNaN(tt.out) && cmplx.IsNaN(got)) && got != tt.out { + t.Errorf("ParseComplex(%q, 128) = %v, %v want %v %v", tt.in, got, err, tt.out, tt.err) } }) - - // Test with parentheses - if tt.wantErr == nil { - str := "(" + tt.str + ")" - - t.Run(str, func(t *testing.T) { - got, err := ParseComplex(str, 128) - if err != nil { - t.Fatalf("Error mismatch\nGot: %v\nWant: %v", err, nil) - } - - if !(cmplx.IsNaN(tt.want) && cmplx.IsNaN(got)) && got != tt.want { - t.Fatalf("Result mismatch\nGot: %v\nWant: %v", got, tt.want) - } - }) - } } } - -func mustFormatComplex(r, i float64) string { - s1 := FormatFloat(r, 'x', -1, 64) - s2 := FormatFloat(i, 'x', -1, 64) - - if i >= 0 { - return s1 + "+" + s2 + "i" - } - - return s1 + s2 + "i" -} From ceaa1722d5ce1af34561b7069c08424a9f904a1e Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 6 May 2020 02:54:55 +1000 Subject: [PATCH 37/47] - partially fix up ErrRange problem --- src/strconv/atoc.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 542076176cee6a..c6fd9952df67a0 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -4,6 +4,8 @@ package strconv +import "math" + const fnParseComplex = "ParseComplex" func convErr(err error, s string) error { @@ -50,6 +52,12 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // Read real part (possibly imaginary part if followed by 'i'). re, n, err := parseFloatPrefix(s, size) if err != nil { + if x, ok := err.(*NumError); ok && x.Err == ErrRange { + if re >= 0 { + return complex(math.Inf(1), 0), x + } + return complex(math.Inf(-1), 0), x + } return 0, convErr(err, orig) } s = s[n:] @@ -82,6 +90,12 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // Read imaginary part. im, n, err := parseFloatPrefix(s, size) if err != nil { + if x, ok := err.(*NumError); ok && x.Err == ErrRange { + if im >= 0 { + return complex(0, math.Inf(1)), x + } + return complex(0, math.Inf(-1)), x + } return 0, convErr(err, orig) } s = s[n:] From 933c9846fbaf8e18430e19af986eaa950d53a906 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 6 May 2020 03:16:54 +1000 Subject: [PATCH 38/47] - changed back: FatalF -> ErrorF -> FatalF (When in Run, FatalF doesn't stop entire test) --- src/strconv/atoc_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 22a22b01e00ae9..5141e0a869186e 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -266,11 +266,11 @@ func TestParseComplex(t *testing.T) { t.Run(tt.in, func(t *testing.T) { got, err := ParseComplex(tt.in, 128) if g, w := err, tt.err; !reflect.DeepEqual(g, w) { - t.Errorf("ParseComplex(%q, 128) = %v, %v want %v %v", tt.in, got, err, tt.out, tt.err) + t.Fatalf("ParseComplex(%q, 128) = %v, %v want %v %v", tt.in, got, err, tt.out, tt.err) } if !(cmplx.IsNaN(tt.out) && cmplx.IsNaN(got)) && got != tt.out { - t.Errorf("ParseComplex(%q, 128) = %v, %v want %v %v", tt.in, got, err, tt.out, tt.err) + t.Fatalf("ParseComplex(%q, 128) = %v, %v want %v %v", tt.in, got, err, tt.out, tt.err) } }) } From afccb3f3302b1209aac75f0f65e0296b8e7c781a Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 6 May 2020 03:29:00 +1000 Subject: [PATCH 39/47] - partial fix of NaN issue --- src/strconv/atoc.go | 23 +++++++++++++---------- src/strconv/atoc_test.go | 12 +++++++++--- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index c6fd9952df67a0..b8229c0700f664 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -4,8 +4,6 @@ package strconv -import "math" - const fnParseComplex = "ParseComplex" func convErr(err error, s string) error { @@ -49,16 +47,16 @@ func ParseComplex(s string, bitSize int) (complex128, error) { s = s[1 : len(s)-1] } + var encounteredRealErrRange error + // Read real part (possibly imaginary part if followed by 'i'). re, n, err := parseFloatPrefix(s, size) if err != nil { if x, ok := err.(*NumError); ok && x.Err == ErrRange { - if re >= 0 { - return complex(math.Inf(1), 0), x - } - return complex(math.Inf(-1), 0), x + encounteredRealErrRange = convErr(err, orig) + } else { + return 0, convErr(err, orig) } - return 0, convErr(err, orig) } s = s[n:] @@ -91,10 +89,11 @@ func ParseComplex(s string, bitSize int) (complex128, error) { im, n, err := parseFloatPrefix(s, size) if err != nil { if x, ok := err.(*NumError); ok && x.Err == ErrRange { - if im >= 0 { - return complex(0, math.Inf(1)), x + if encounteredRealErrRange != nil { + return complex(re, im), convErr(err, orig) + } else { + return complex(0, im), convErr(err, orig) } - return complex(0, math.Inf(-1)), x } return 0, convErr(err, orig) } @@ -102,5 +101,9 @@ func ParseComplex(s string, bitSize int) (complex128, error) { if s != "i" { return 0, syntaxError(fnParseComplex, orig) } + + if encounteredRealErrRange != nil { + return complex(re, 0), encounteredRealErrRange + } return complex(re, im), nil } diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 5141e0a869186e..bd58daa8908b12 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -228,9 +228,15 @@ func TestParseComplex(t *testing.T) { // a little too large {"1e308+1e308i", 1e+308 + 1e+308i, nil}, - {"2e308", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"1e309", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"0x1p1025", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"2e308+2e308i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"1e309+1e309i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"0x1p1025+0x1p1025i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"2e308", complex(math.Inf(1), 0), ErrRange}, + {"1e309", complex(math.Inf(1), 0), ErrRange}, + {"0x1p1025", complex(math.Inf(1), 0), ErrRange}, + {"2e308i", complex(0, math.Inf(1)), ErrRange}, + {"1e309i", complex(0, math.Inf(1)), ErrRange}, + {"0x1p1025i", complex(0, math.Inf(1)), ErrRange}, // way too large {"1e310+1e310i", complex(math.Inf(1), math.Inf(1)), ErrRange}, From a8fe3faf55fe4605e1896a606c4b7b384075f7f5 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 6 May 2020 03:38:43 +1000 Subject: [PATCH 40/47] - ammended ErrRange issue --- src/strconv/atoc.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index b8229c0700f664..51429d22ac39d0 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -62,6 +62,9 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // If we have nothing left, we're done. if len(s) == 0 { + if encounteredRealErrRange != nil { + return complex(re, 0), encounteredRealErrRange + } return complex(re, 0), nil } @@ -78,6 +81,9 @@ func ParseComplex(s string, bitSize int) (complex128, error) { case 'i': // If 'i' is the last character, we only have an imaginary part. if len(s) == 1 { + if encounteredRealErrRange != nil { + return complex(0, re), encounteredRealErrRange + } return complex(0, re), nil } fallthrough From fc2663af46467038db3d3a15621c586cdbfe3264 Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 6 May 2020 12:11:29 +1000 Subject: [PATCH 41/47] - amend ParseComplex to cater for ErrRange --- src/strconv/atoc.go | 46 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 51429d22ac39d0..238270a6ef3f73 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -6,18 +6,23 @@ package strconv const fnParseComplex = "ParseComplex" -func convErr(err error, s string) error { +// convErr splits an error returned by parseFloatPrefix +// into a syntax or range error for ParseComplex. +func convErr(err error, s string) (syntax, range_ error) { if x, ok := err.(*NumError); ok { x.Func = fnParseComplex x.Num = s + if x.Err == ErrRange { + return nil, x + } } - return err + return err, nil } // ParseComplex converts the string s to a complex number // with the precision specified by bitSize: 64 for complex64, or 128 for complex128. // When bitSize=64, the result still has type complex128, but it will be -// convertible to complex64 without changing its value. +// convertible to complex64 without changing it's value. // // The number represented by s must be of the form N, Ni, or N±Ni, where N stands // for a floating-point number as recognized by ParseFloat, and i is the imaginary @@ -47,25 +52,21 @@ func ParseComplex(s string, bitSize int) (complex128, error) { s = s[1 : len(s)-1] } - var encounteredRealErrRange error + var pending error // pending range error, or nil // Read real part (possibly imaginary part if followed by 'i'). re, n, err := parseFloatPrefix(s, size) if err != nil { - if x, ok := err.(*NumError); ok && x.Err == ErrRange { - encounteredRealErrRange = convErr(err, orig) - } else { - return 0, convErr(err, orig) + err, pending = convErr(err, orig) + if err != nil { + return 0, err } } s = s[n:] // If we have nothing left, we're done. if len(s) == 0 { - if encounteredRealErrRange != nil { - return complex(re, 0), encounteredRealErrRange - } - return complex(re, 0), nil + return complex(re, 0), pending } // Otherwise, look at the next character. @@ -81,10 +82,7 @@ func ParseComplex(s string, bitSize int) (complex128, error) { case 'i': // If 'i' is the last character, we only have an imaginary part. if len(s) == 1 { - if encounteredRealErrRange != nil { - return complex(0, re), encounteredRealErrRange - } - return complex(0, re), nil + return complex(0, re), pending } fallthrough default: @@ -94,22 +92,14 @@ func ParseComplex(s string, bitSize int) (complex128, error) { // Read imaginary part. im, n, err := parseFloatPrefix(s, size) if err != nil { - if x, ok := err.(*NumError); ok && x.Err == ErrRange { - if encounteredRealErrRange != nil { - return complex(re, im), convErr(err, orig) - } else { - return complex(0, im), convErr(err, orig) - } + err, pending = convErr(err, orig) + if err != nil { + return 0, err } - return 0, convErr(err, orig) } s = s[n:] if s != "i" { return 0, syntaxError(fnParseComplex, orig) } - - if encounteredRealErrRange != nil { - return complex(re, 0), encounteredRealErrRange - } - return complex(re, im), nil + return complex(re, im), pending } From 5dcb0fb4f02afc67e0c4f03fd5a7fdbb6b6554bb Mon Sep 17 00:00:00 2001 From: pj Date: Wed, 6 May 2020 12:38:35 +1000 Subject: [PATCH 42/47] - update ParseFloat docs to reveal "Infinity" --- src/strconv/atof.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/strconv/atof.go b/src/strconv/atof.go index 0de48bd2d9246a..7de0aeced08e4a 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -669,9 +669,8 @@ func atof64(s string) (f float64, err error) { // away from the largest floating point number of the given size, // ParseFloat returns f = ±Inf, err.Err = ErrRange. // -// ParseFloat recognizes the strings "NaN", "Infinity", "+Infinity", "-Infinity", -// "Inf", +Inf", and "-Inf" as their respective special floating point values. -// It ignores case when matching. +// 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 { f, err := atof32(s) From 42cb8e0f04ebaad326e96b14d4afa284ca560a7b Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 8 May 2020 01:55:19 +1000 Subject: [PATCH 43/47] - revert docs back to correct possessive case --- src/strconv/atoc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 238270a6ef3f73..55b7c23ee7aa50 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -22,7 +22,7 @@ func convErr(err error, s string) (syntax, range_ error) { // ParseComplex converts the string s to a complex number // with the precision specified by bitSize: 64 for complex64, or 128 for complex128. // When bitSize=64, the result still has type complex128, but it will be -// convertible to complex64 without changing it's value. +// convertible to complex64 without changing its value. // // The number represented by s must be of the form N, Ni, or N±Ni, where N stands // for a floating-point number as recognized by ParseFloat, and i is the imaginary From f4eebf8ccadbc79dd24209a4d28ac0d71356d376 Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 8 May 2020 02:00:37 +1000 Subject: [PATCH 44/47] - reduce tests for zeros/infs/NaN --- src/strconv/atoc_test.go | 96 +++++++++++----------------------------- 1 file changed, 26 insertions(+), 70 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index bd58daa8908b12..3e5a0fc3db6b53 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -12,6 +12,12 @@ import ( "testing" ) +var ( + infpp = complex(math.Inf(+1), math.Inf(+1)) + infpm = complex(math.Inf(+1), math.Inf(-1)) + infmp = complex(math.Inf(-1), math.Inf(+1)) + infmm = complex(math.Inf(-1), math.Inf(-1)) +) type atocTest struct { in string out complex128 @@ -29,103 +35,53 @@ func TestParseComplex(t *testing.T) { {"10 + 5i", 0, ErrSyntax}, {"3+3+5.5", 0, ErrSyntax}, {"3+3+5.5i", 0, ErrSyntax}, + {"3+3+5.5I", 0, ErrSyntax}, // Parentheses + {"()", 0, ErrSyntax}, + {"(0)", 0, nil}, + {"(i)", 0, ErrSyntax}, + {"(1i)", 1i, nil}, {"(3.0+5.5i)", 3.0 + 5.5i, nil}, {"(3.0+5.5i", 0, ErrSyntax}, + {"3.0+5.5i)", 0, ErrSyntax}, // NaNs {"NaN", complex(math.NaN(), 0), nil}, - {"NaNi", complex(0, math.NaN()), nil}, - {"NaN+NaNi", complex(math.NaN(), math.NaN()), nil}, - {"NaN++NaNi", 0, ErrSyntax}, + {"NANi", complex(0, math.NaN()), nil}, + {"nan+nAni", complex(math.NaN(), math.NaN()), nil}, {"+NaN", 0, ErrSyntax}, - {"++NaN", 0, ErrSyntax}, {"-NaN", 0, ErrSyntax}, - {"+NaNi", 0, ErrSyntax}, - {"-NaNi", 0, ErrSyntax}, {"NaN-NaNi", 0, ErrSyntax}, // Infs - {"Infi", complex(0, math.Inf(1)), nil}, - {"infi", complex(0, math.Inf(1)), nil}, - {"inf i", 0, ErrSyntax}, - {"-Infi", complex(0, math.Inf(-1)), nil}, - {"-infi", complex(0, math.Inf(-1)), nil}, - {"-inf i", 0, ErrSyntax}, {"Inf", complex(math.Inf(1), 0), nil}, - {"-Inf", complex(math.Inf(-1), 0), nil}, - {"-Inf-Infi", complex(math.Inf(-1), math.Inf(-1)), nil}, - {"-Inf+Infi", complex(math.Inf(-1), math.Inf(1)), nil}, - {"-Inf-Inf i", 0, ErrSyntax}, - {"-Inf+Inf i", 0, ErrSyntax}, - {"-Inf- Inf i", 0, ErrSyntax}, - {"-Inf+ Inf i", 0, ErrSyntax}, - {"-Inf- Infi", 0, ErrSyntax}, - {"-Inf+ Infi", 0, ErrSyntax}, + {"+inf", complex(math.Inf(1), 0), nil}, + {"Infinity", complex(math.Inf(1), 0), nil}, + {"+INFINITY", complex(math.Inf(1), 0), nil}, + {"-infinity", complex(math.Inf(-1), 0), nil}, + {"Inf+Infi", complex(math.Inf(1), math.Inf(1)), nil}, + {"+Inf-Infi", complex(math.Inf(1), math.Inf(-1)), nil}, + {"-Infinity+Infi", complex(math.Inf(-1), math.Inf(1)), nil}, + {"inf - inf", 0, ErrSyntax}, // Zeros {"0", 0, nil}, {"0i", 0, nil}, - {"0+0i", 0, nil}, - {"0.0", 0, nil}, - {"0.0i", 0, nil}, - {"0.0+0.0i", 0, nil}, - {"0.0-0.0i", 0, nil}, + {"-0.0i", 0, nil}, + {"0+0.0i", 0, nil}, + {"0e+0i", 0, nil}, + {"0e-0+0i", 0, nil}, {"-0.0-0.0i", 0, nil}, - {"-0.0+0.0i", 0, nil}, - {"0e0", 0, nil}, - {"-0e0", 0, nil}, - {"+0e0", 0, nil}, - {"0e-0", 0, nil}, - {"-0e-0", 0, nil}, - {"+0e-0", 0, nil}, - {"0e+0", 0, nil}, - {"-0e+0", 0, nil}, - {"+0e+0", 0, nil}, {"0e+01234567890123456789", 0, nil}, - {"0.00e-01234567890123456789", 0, nil}, - {"-0e+01234567890123456789", 0, nil}, - {"-0.00e-01234567890123456789", 0, nil}, - {"0x0p+01234567890123456789", 0, nil}, - {"0x0.00p-01234567890123456789", 0, nil}, - {"-0x0p+01234567890123456789", 0, nil}, - {"-0x0.00p-01234567890123456789", 0, nil}, - {"0e0i", 0, nil}, - {"-0e0i", 0, nil}, - {"+0e0i", 0, nil}, - {"0e-0i", 0, nil}, - {"-0e-0i", 0, nil}, - {"+0e-0i", 0, nil}, - {"0e+0i", 0, nil}, - {"-0e+0i", 0, nil}, - {"+0e+0i", 0, nil}, - {"0e+01234567890123456789i", 0, nil}, - {"0.00e-01234567890123456789i", 0, nil}, - {"-0e+01234567890123456789i", 0, nil}, - {"-0.00e-01234567890123456789i", 0, nil}, {"0x0p+01234567890123456789i", 0, nil}, {"0x0.00p-01234567890123456789i", 0, nil}, - {"-0x0p+01234567890123456789i", 0, nil}, - {"-0x0.00p-01234567890123456789i", 0, nil}, - {"0+0i", 0, nil}, - {"0e0+0e0i", 0, nil}, - {"-0e0-0e0i", 0, nil}, - {"+0e0+0e0i", 0, nil}, - {"0e-0+0e-0i", 0, nil}, - {"-0e-0-0e-0i", 0, nil}, {"+0e-0+0e-0i", 0, nil}, {"0e+0+0e+0i", 0, nil}, {"-0e+0-0e+0i", 0, nil}, - {"+0e+0+0e+0i", 0, nil}, - {"0e+01234567890123456789+0e+01234567890123456789i", 0, nil}, - {"0.00e-01234567890123456789+0.00e-01234567890123456789i", 0, nil}, - {"-0e+01234567890123456789-0e+01234567890123456789i", 0, nil}, {"-0.00e-01234567890123456789-0.00e-01234567890123456789i", 0, nil}, {"0x0p+01234567890123456789+0x0p+01234567890123456789i", 0, nil}, {"0x0.00p-01234567890123456789+0x0.00p-01234567890123456789i", 0, nil}, - {"-0x0p+01234567890123456789-0x0p+01234567890123456789i", 0, nil}, - {"-0x0.00p-01234567890123456789-0x0.00p-01234567890123456789i", 0, nil}, {"0.1", 0.1, nil}, {"0.1i", 0 + 0.1i, nil}, From 96cea1a65572d05c2a090c2f13c2a9fd6a051e92 Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 8 May 2020 02:02:20 +1000 Subject: [PATCH 45/47] - remove some cases that were already tested --- src/strconv/atoc_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index 3e5a0fc3db6b53..b77917ae16979d 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -98,8 +98,6 @@ func TestParseComplex(t *testing.T) { {"-i", 0, ErrSyntax}, {"+3+1i", 3 + 1i, nil}, {"30+3i", 30 + 3i, nil}, - {"30+3i)", 0, ErrSyntax}, - {"(30+4i", 0, ErrSyntax}, {"+3e+3-3e+3i", 3e+3 - 3e+3i, nil}, {"+3e+3+3e+3i", 3e+3 + 3e+3i, nil}, {"+3e+3+3e+3i+", 0, ErrSyntax}, From d45bbbbe4f5ff1583594cf0d2d13b817cc61d4ef Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 8 May 2020 02:08:37 +1000 Subject: [PATCH 46/47] - use global variables for math.Inf usage - add complex (math.Inf(1), math.Inf(-1)) case and reverse --- src/strconv/atoc_test.go | 76 +++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index b77917ae16979d..e81b03a04370d5 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -153,16 +153,18 @@ func TestParseComplex(t *testing.T) { // ErrRange // next float64 - too large - {"1.7976931348623159e308+1.7976931348623159e308i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-1.7976931348623159e308-1.7976931348623159e308i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"0x1p1024+0x1p1024i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-0x1p1024-0x1p1024i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"0x2p1023+0x2p1023i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-0x2p1023-0x2p1023i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"0x.1p1028+0x.1p1028i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-0x.1p1028-0x.1p1028i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"0x.2p1027+0x.2p1027i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-0x.2p1027-0x.2p1027i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"1.7976931348623159e308+1.7976931348623159e308i", infpp, ErrRange}, + {"-1.7976931348623159e308-1.7976931348623159e308i", infmm, ErrRange}, + {"0x1p1024+0x1p1024i", infpp, ErrRange}, + {"0x1p1024-0x1p1024i", infpm, ErrRange}, + {"-0x1p1024+0x1p1024i", infmp, ErrRange}, + {"-0x1p1024-0x1p1024i", infmm, ErrRange}, + {"0x2p1023+0x2p1023i", infpp, ErrRange}, + {"-0x2p1023-0x2p1023i", infmm, ErrRange}, + {"0x.1p1028+0x.1p1028i", infpp, ErrRange}, + {"-0x.1p1028-0x.1p1028i", infmm, ErrRange}, + {"0x.2p1027+0x.2p1027i", infpp, ErrRange}, + {"-0x.2p1027-0x.2p1027i", infmm, ErrRange}, // the border is ...158079 // borderline - okay @@ -171,20 +173,20 @@ func TestParseComplex(t *testing.T) { {"0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil}, {"-0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 - 1.7976931348623157e+308i, nil}, // borderline - too large - {"1.797693134862315808e308+1.797693134862315808e308i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-1.797693134862315808e308-1.797693134862315808e308i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"0x1.fffffffffffff8p1023+0x1.fffffffffffff8p1023i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-0x1.fffffffffffff8p1023-0x1.fffffffffffff8p1023i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"0x1fffffffffffff.8p+971+0x1fffffffffffff.8p+971i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-0x1fffffffffffff8p+967-0x1fffffffffffff8p+967i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"0x.1fffffffffffff8p1027+0x.1fffffffffffff8p1027i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-0x.1fffffffffffff9p1027-0x.1fffffffffffff9p1027i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"1.797693134862315808e308+1.797693134862315808e308i", infpp, ErrRange}, + {"-1.797693134862315808e308-1.797693134862315808e308i", infmm, ErrRange}, + {"0x1.fffffffffffff8p1023+0x1.fffffffffffff8p1023i", infpp, ErrRange}, + {"-0x1.fffffffffffff8p1023-0x1.fffffffffffff8p1023i", infmm, ErrRange}, + {"0x1fffffffffffff.8p+971+0x1fffffffffffff.8p+971i", infpp, ErrRange}, + {"-0x1fffffffffffff8p+967-0x1fffffffffffff8p+967i", infmm, ErrRange}, + {"0x.1fffffffffffff8p1027+0x.1fffffffffffff8p1027i", infpp, ErrRange}, + {"-0x.1fffffffffffff9p1027-0x.1fffffffffffff9p1027i", infmm, ErrRange}, // a little too large {"1e308+1e308i", 1e+308 + 1e+308i, nil}, - {"2e308+2e308i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"1e309+1e309i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"0x1p1025+0x1p1025i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"2e308+2e308i", infpp, ErrRange}, + {"1e309+1e309i", infpp, ErrRange}, + {"0x1p1025+0x1p1025i", infpp, ErrRange}, {"2e308", complex(math.Inf(1), 0), ErrRange}, {"1e309", complex(math.Inf(1), 0), ErrRange}, {"0x1p1025", complex(math.Inf(1), 0), ErrRange}, @@ -193,28 +195,28 @@ func TestParseComplex(t *testing.T) { {"0x1p1025i", complex(0, math.Inf(1)), ErrRange}, // way too large - {"1e310+1e310i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-1e310-1e310i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"1e400+1e400i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-1e400-1e400i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"1e400000+1e400000i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-1e400000-1e400000i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"0x1p1030+0x1p1030i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"0x1p2000+0x1p2000i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"0x1p2000000000+0x1p2000000000i", complex(math.Inf(1), math.Inf(1)), ErrRange}, - {"-0x1p1030-0x1p1030i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"-0x1p2000-0x1p2000i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, - {"-0x1p2000000000-0x1p2000000000i", complex(math.Inf(-1), math.Inf(-1)), ErrRange}, + {"1e310+1e310i", infpp, ErrRange}, + {"-1e310-1e310i", infmm, ErrRange}, + {"1e400+1e400i", infpp, ErrRange}, + {"-1e400-1e400i", infmm, ErrRange}, + {"1e400000+1e400000i", infpp, ErrRange}, + {"-1e400000-1e400000i", infmm, ErrRange}, + {"0x1p1030+0x1p1030i", infpp, ErrRange}, + {"0x1p2000+0x1p2000i", infpp, ErrRange}, + {"0x1p2000000000+0x1p2000000000i", infpp, ErrRange}, + {"-0x1p1030-0x1p1030i", infmm, ErrRange}, + {"-0x1p2000-0x1p2000i", infmm, ErrRange}, + {"-0x1p2000000000-0x1p2000000000i", infmm, ErrRange}, // try to overflow exponent {"1e-4294967296+1e-4294967296i", 0, nil}, - {"1e+4294967296+1e+4294967296i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"1e+4294967296+1e+4294967296i", infpp, ErrRange}, {"1e-18446744073709551616+1e-18446744073709551616i", 0, nil}, - {"1e+18446744073709551616+1e+18446744073709551616i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"1e+18446744073709551616+1e+18446744073709551616i", infpp, ErrRange}, {"0x1p-4294967296+0x1p-4294967296i", 0, nil}, - {"0x1p+4294967296+0x1p+4294967296i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"0x1p+4294967296+0x1p+4294967296i", infpp, ErrRange}, {"0x1p-18446744073709551616+0x1p-18446744073709551616i", 0, nil}, - {"0x1p+18446744073709551616+0x1p+18446744073709551616i", complex(math.Inf(1), math.Inf(1)), ErrRange}, + {"0x1p+18446744073709551616+0x1p+18446744073709551616i", infpp, ErrRange}, } for _, tt := range tests { From d33605719e1902c80a04a50f3c2c47da3cfd17ea Mon Sep 17 00:00:00 2001 From: pj Date: Fri, 8 May 2020 22:47:28 +1000 Subject: [PATCH 47/47] - final tests for atoc --- src/strconv/atoc_test.go | 204 +++++++++++++++------------------------ 1 file changed, 80 insertions(+), 124 deletions(-) diff --git a/src/strconv/atoc_test.go b/src/strconv/atoc_test.go index e81b03a04370d5..5c817a2e44b6f5 100644 --- a/src/strconv/atoc_test.go +++ b/src/strconv/atoc_test.go @@ -5,19 +5,24 @@ package strconv_test import ( - . "strconv" "math" "math/cmplx" "reflect" + . "strconv" "testing" ) var ( + infp0 = complex(math.Inf(+1), 0) + infm0 = complex(math.Inf(-1), 0) + inf0p = complex(0, math.Inf(+1)) + inf0m = complex(0, math.Inf(-1)) infpp = complex(math.Inf(+1), math.Inf(+1)) infpm = complex(math.Inf(+1), math.Inf(-1)) infmp = complex(math.Inf(-1), math.Inf(+1)) infmm = complex(math.Inf(-1), math.Inf(-1)) ) + type atocTest struct { in string out complex128 @@ -25,27 +30,30 @@ type atocTest struct { } func TestParseComplex(t *testing.T) { + tests := []atocTest{ - // Clear Invalids + // Clearly invalid {"", 0, ErrSyntax}, {" ", 0, ErrSyntax}, {"(", 0, ErrSyntax}, {")", 0, ErrSyntax}, - {"foo", 0, ErrSyntax}, + {"i", 0, ErrSyntax}, + {"+i", 0, ErrSyntax}, + {"-i", 0, ErrSyntax}, + {"1I", 0, ErrSyntax}, {"10 + 5i", 0, ErrSyntax}, - {"3+3+5.5", 0, ErrSyntax}, - {"3+3+5.5i", 0, ErrSyntax}, - {"3+3+5.5I", 0, ErrSyntax}, - + {"3+", 0, ErrSyntax}, + {"3+5", 0, ErrSyntax}, + {"3+5+5i", 0, ErrSyntax}, // Parentheses {"()", 0, ErrSyntax}, - {"(0)", 0, nil}, {"(i)", 0, ErrSyntax}, + {"(0)", 0, nil}, {"(1i)", 1i, nil}, {"(3.0+5.5i)", 3.0 + 5.5i, nil}, + {"(1)+1i", 0, ErrSyntax}, {"(3.0+5.5i", 0, ErrSyntax}, {"3.0+5.5i)", 0, ErrSyntax}, - // NaNs {"NaN", complex(math.NaN(), 0), nil}, {"NANi", complex(0, math.NaN()), nil}, @@ -53,18 +61,19 @@ func TestParseComplex(t *testing.T) { {"+NaN", 0, ErrSyntax}, {"-NaN", 0, ErrSyntax}, {"NaN-NaNi", 0, ErrSyntax}, - // Infs - {"Inf", complex(math.Inf(1), 0), nil}, - {"+inf", complex(math.Inf(1), 0), nil}, - {"Infinity", complex(math.Inf(1), 0), nil}, - {"+INFINITY", complex(math.Inf(1), 0), nil}, - {"-infinity", complex(math.Inf(-1), 0), nil}, - {"Inf+Infi", complex(math.Inf(1), math.Inf(1)), nil}, - {"+Inf-Infi", complex(math.Inf(1), math.Inf(-1)), nil}, - {"-Infinity+Infi", complex(math.Inf(-1), math.Inf(1)), nil}, - {"inf - inf", 0, ErrSyntax}, - + {"Inf", infp0, nil}, + {"+inf", infp0, nil}, + {"-inf", infm0, nil}, + {"Infinity", infp0, nil}, + {"+INFINITY", infp0, nil}, + {"-infinity", infm0, nil}, + {"+infi", inf0p, nil}, + {"0-infinityi", inf0m, nil}, + {"Inf+Infi", infpp, nil}, + {"+Inf-Infi", infpm, nil}, + {"-Infinity+Infi", infmp, nil}, + {"inf-inf", 0, ErrSyntax}, // Zeros {"0", 0, nil}, {"0i", 0, nil}, @@ -73,16 +82,13 @@ func TestParseComplex(t *testing.T) { {"0e+0i", 0, nil}, {"0e-0+0i", 0, nil}, {"-0.0-0.0i", 0, nil}, - {"0e+01234567890123456789", 0, nil}, - {"0x0p+01234567890123456789i", 0, nil}, - {"0x0.00p-01234567890123456789i", 0, nil}, + {"0e+012345", 0, nil}, + {"0x0p+012345i", 0, nil}, + {"0x0.00p-012345i", 0, nil}, {"+0e-0+0e-0i", 0, nil}, {"0e+0+0e+0i", 0, nil}, {"-0e+0-0e+0i", 0, nil}, - {"-0.00e-01234567890123456789-0.00e-01234567890123456789i", 0, nil}, - {"0x0p+01234567890123456789+0x0p+01234567890123456789i", 0, nil}, - {"0x0.00p-01234567890123456789+0x0.00p-01234567890123456789i", 0, nil}, - + // Regular non-zeroes {"0.1", 0.1, nil}, {"0.1i", 0 + 0.1i, nil}, {"0.123", 0.123, nil}, @@ -92,147 +98,97 @@ func TestParseComplex(t *testing.T) { {"+99", 99, nil}, {"-99", -99, nil}, {"+1i", 1i, nil}, - {"i", 0, ErrSyntax}, - {"+i", 0, ErrSyntax}, {"-1i", -1i, nil}, - {"-i", 0, ErrSyntax}, {"+3+1i", 3 + 1i, nil}, {"30+3i", 30 + 3i, nil}, {"+3e+3-3e+3i", 3e+3 - 3e+3i, nil}, {"+3e+3+3e+3i", 3e+3 + 3e+3i, nil}, {"+3e+3+3e+3i+", 0, ErrSyntax}, - + // Separators + {"0.1", 0.1, nil}, + {"0.1i", 0 + 0.1i, nil}, + {"0.1_2_3", 0.123, nil}, + {"+0x_3p3i", 0x3p3i, nil}, + {"0x_10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil}, + {"+0x_1_0.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil}, + {"0x10.3p+8-0x_3p3i", 0x10.3p+8 - 0x3p3i, nil}, // Hexadecimals - {"0x10.3p-8+0x3p3i",0x10.3p-8+0x3p3i, nil}, - {"+0x10.3p-8+0x3p3i",0x10.3p-8+0x3p3i, nil}, - {"0x10.3p+8-0x3p3i",0x10.3p+8-0x3p3i, nil}, + {"0x10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil}, + {"+0x10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil}, + {"0x10.3p+8-0x3p3i", 0x10.3p+8 - 0x3p3i, nil}, {"0x1p0", 1, nil}, {"0x1p1", 2, nil}, {"0x1p-1", 0.5, nil}, {"0x1ep-1", 15, nil}, {"-0x1ep-1", -15, nil}, - {"-0x1_ep-1", -15, nil}, - {"0x1p-200", 6.223015277861142e-61, nil}, - {"0x1p200", 1.6069380442589903e+60, nil}, - {"0x1fFe2.p0", 131042, nil}, - {"0x1fFe2.P0", 131042, nil}, {"-0x2p3", -16, nil}, - {"0x0.fp4", 15, nil}, - {"0x0.fp0", 0.9375, nil}, {"0x1e2", 0, ErrSyntax}, {"1p2", 0, ErrSyntax}, - {"0x1p0i", 1i, nil}, - {"0x1p1i", 2i, nil}, - {"0x1p-1i", 0.5i, nil}, - {"0x1ep-1i", 15i, nil}, - {"-0x1ep-1i", -15i, nil}, - {"-0x1_ep-1i", -15i, nil}, - {"0x1p-200i", 6.223015277861142e-61i, nil}, - {"0x1p200i", 1.6069380442589903e+60i, nil}, - {"0x1fFe2.p0i", 131042i, nil}, - {"0x1fFe2.P0i", 131042i, nil}, - {"-0x2p3i", -16i, nil}, - {"0x0.fp4i", 15i, nil}, - {"0x0.fp0i", 0.9375i, nil}, {"0x1e2i", 0, ErrSyntax}, - {"1p2i", 0, ErrSyntax}, - {"0x1p0+0x1p0i", 1 + 1i, nil}, - {"0x1p1+0x1p1i", 2 + 2i, nil}, - {"0x1p-1+0x1p-1i", 0.5 + 0.5i, nil}, - {"0x1ep-1+0x1ep-1i", 15 + 15i, nil}, - {"-0x1ep-1-0x1ep-1i", -15 - 15i, nil}, - {"-0x1_ep-1-0x1_ep-1i", -15 - 15i, nil}, - {"0x1p-200+0x1p-200i", 6.223015277861142e-61 + 6.223015277861142e-61i, nil}, - {"0x1p200+0x1p200i", 1.6069380442589903e+60 + 1.6069380442589903e+60i, nil}, - {"0x1fFe2.p0+0x1fFe2.p0i", 131042 + 131042i, nil}, - {"0x1fFe2.P0+0x1fFe2.P0i", 131042 + 131042i, nil}, - {"-0x2p3-0x2p3i", -16 - 16i, nil}, - {"0x0.fp4+0x0.fp4i", 15 + 15i, nil}, - {"0x0.fp0+0x0.fp0i", 0.9375 + 0.9375i, nil}, - // ErrRange - // next float64 - too large - {"1.7976931348623159e308+1.7976931348623159e308i", infpp, ErrRange}, - {"-1.7976931348623159e308-1.7976931348623159e308i", infmm, ErrRange}, - {"0x1p1024+0x1p1024i", infpp, ErrRange}, - {"0x1p1024-0x1p1024i", infpm, ErrRange}, + {"+0x1p1024", infp0, ErrRange}, + {"-0x1p1024", infm0, ErrRange}, + {"+0x1p1024i", inf0p, ErrRange}, + {"-0x1p1024i", inf0m, ErrRange}, + {"+0x1p1024+0x1p1024i", infpp, ErrRange}, + {"+0x1p1024-0x1p1024i", infpm, ErrRange}, {"-0x1p1024+0x1p1024i", infmp, ErrRange}, {"-0x1p1024-0x1p1024i", infmm, ErrRange}, - {"0x2p1023+0x2p1023i", infpp, ErrRange}, - {"-0x2p1023-0x2p1023i", infmm, ErrRange}, - {"0x.1p1028+0x.1p1028i", infpp, ErrRange}, - {"-0x.1p1028-0x.1p1028i", infmm, ErrRange}, - {"0x.2p1027+0x.2p1027i", infpp, ErrRange}, - {"-0x.2p1027-0x.2p1027i", infmm, ErrRange}, - // the border is ...158079 // borderline - okay - {"1.7976931348623158e308+1.7976931348623158e308i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil}, - {"-1.7976931348623158e308-1.7976931348623158e308i", -1.7976931348623157e+308 - 1.7976931348623157e+308i, nil}, - {"0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil}, + {"+0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil}, + {"+0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 - 1.7976931348623157e+308i, nil}, + {"-0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 + 1.7976931348623157e+308i, nil}, {"-0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 - 1.7976931348623157e+308i, nil}, // borderline - too large - {"1.797693134862315808e308+1.797693134862315808e308i", infpp, ErrRange}, - {"-1.797693134862315808e308-1.797693134862315808e308i", infmm, ErrRange}, - {"0x1.fffffffffffff8p1023+0x1.fffffffffffff8p1023i", infpp, ErrRange}, - {"-0x1.fffffffffffff8p1023-0x1.fffffffffffff8p1023i", infmm, ErrRange}, - {"0x1fffffffffffff.8p+971+0x1fffffffffffff.8p+971i", infpp, ErrRange}, + {"+0x1.fffffffffffff8p1023", infp0, ErrRange}, + {"-0x1fffffffffffff.8p+971", infm0, ErrRange}, + {"+0x1.fffffffffffff8p1023i", inf0p, ErrRange}, + {"-0x1fffffffffffff.8p+971i", inf0m, ErrRange}, + {"+0x1.fffffffffffff8p1023+0x1.fffffffffffff8p1023i", infpp, ErrRange}, + {"+0x1.fffffffffffff8p1023-0x1.fffffffffffff8p1023i", infpm, ErrRange}, + {"-0x1fffffffffffff.8p+971+0x1fffffffffffff.8p+971i", infmp, ErrRange}, {"-0x1fffffffffffff8p+967-0x1fffffffffffff8p+967i", infmm, ErrRange}, - {"0x.1fffffffffffff8p1027+0x.1fffffffffffff8p1027i", infpp, ErrRange}, - {"-0x.1fffffffffffff9p1027-0x.1fffffffffffff9p1027i", infmm, ErrRange}, - // a little too large {"1e308+1e308i", 1e+308 + 1e+308i, nil}, {"2e308+2e308i", infpp, ErrRange}, {"1e309+1e309i", infpp, ErrRange}, {"0x1p1025+0x1p1025i", infpp, ErrRange}, - {"2e308", complex(math.Inf(1), 0), ErrRange}, - {"1e309", complex(math.Inf(1), 0), ErrRange}, - {"0x1p1025", complex(math.Inf(1), 0), ErrRange}, - {"2e308i", complex(0, math.Inf(1)), ErrRange}, - {"1e309i", complex(0, math.Inf(1)), ErrRange}, - {"0x1p1025i", complex(0, math.Inf(1)), ErrRange}, - + {"2e308", infp0, ErrRange}, + {"1e309", infp0, ErrRange}, + {"0x1p1025", infp0, ErrRange}, + {"2e308i", inf0p, ErrRange}, + {"1e309i", inf0p, ErrRange}, + {"0x1p1025i", inf0p, ErrRange}, // way too large - {"1e310+1e310i", infpp, ErrRange}, + {"+1e310+1e310i", infpp, ErrRange}, + {"+1e310-1e310i", infpm, ErrRange}, + {"-1e310+1e310i", infmp, ErrRange}, {"-1e310-1e310i", infmm, ErrRange}, - {"1e400+1e400i", infpp, ErrRange}, - {"-1e400-1e400i", infmm, ErrRange}, - {"1e400000+1e400000i", infpp, ErrRange}, - {"-1e400000-1e400000i", infmm, ErrRange}, - {"0x1p1030+0x1p1030i", infpp, ErrRange}, - {"0x1p2000+0x1p2000i", infpp, ErrRange}, - {"0x1p2000000000+0x1p2000000000i", infpp, ErrRange}, - {"-0x1p1030-0x1p1030i", infmm, ErrRange}, - {"-0x1p2000-0x1p2000i", infmm, ErrRange}, - {"-0x1p2000000000-0x1p2000000000i", infmm, ErrRange}, - - // try to overflow exponent + // under/overflow exponent + {"1e-4294967296", 0, nil}, + {"1e-4294967296i", 0, nil}, + {"1e-4294967296+1i", 1i, nil}, + {"1+1e-4294967296i", 1, nil}, {"1e-4294967296+1e-4294967296i", 0, nil}, + {"1e+4294967296", infp0, ErrRange}, + {"1e+4294967296i", inf0p, ErrRange}, {"1e+4294967296+1e+4294967296i", infpp, ErrRange}, - {"1e-18446744073709551616+1e-18446744073709551616i", 0, nil}, - {"1e+18446744073709551616+1e+18446744073709551616i", infpp, ErrRange}, - {"0x1p-4294967296+0x1p-4294967296i", 0, nil}, - {"0x1p+4294967296+0x1p+4294967296i", infpp, ErrRange}, - {"0x1p-18446744073709551616+0x1p-18446744073709551616i", 0, nil}, - {"0x1p+18446744073709551616+0x1p+18446744073709551616i", infpp, ErrRange}, + {"1e+4294967296-1e+4294967296i", infpm, ErrRange}, } - for _, tt := range tests { tt := tt // for capture in Run closures below if tt.err != nil { tt.err = &NumError{Func: "ParseComplex", Num: tt.in, Err: tt.err} } - t.Run(tt.in, func(t *testing.T) { got, err := ParseComplex(tt.in, 128) - if g, w := err, tt.err; !reflect.DeepEqual(g, w) { - t.Fatalf("ParseComplex(%q, 128) = %v, %v want %v %v", tt.in, got, err, tt.out, tt.err) + if !reflect.DeepEqual(err, tt.err) { + t.Fatalf("ParseComplex(%q, 128) = %v, %v want %v, %v", tt.in, got, err, tt.out, tt.err) } - if !(cmplx.IsNaN(tt.out) && cmplx.IsNaN(got)) && got != tt.out { - t.Fatalf("ParseComplex(%q, 128) = %v, %v want %v %v", tt.in, got, err, tt.out, tt.err) + t.Fatalf("ParseComplex(%q, 128) = %v, %v want %v, %v", tt.in, got, err, tt.out, tt.err) } }) }