Skip to content

Commit f36e1ad

Browse files
odeke-emgriesemer
authored andcommitted
math/big: implement Float.Scan, type assert fmt interfaces to enforce docs
Implements Float.Scan which satisfies fmt.Scanner interface. Also enforces docs' interface implementation claims with compile time type assertions, that is: + Float always implements fmt.Formatter and fmt.Scanner + Int always implements fmt.Formatter and fmt.Scanner + Rat always implements fmt.Formatter which will ensure that the API claims are strictly matched. Also note that Float.Scan doesn't handle ±Inf. Fixes #17391 Change-Id: I3d3dfbe7f602066975c7a7794fe25b4c645440ce Reviewed-on: https://go-review.googlesource.com/30723 Reviewed-by: Robert Griesemer <[email protected]>
1 parent ead08e9 commit f36e1ad

File tree

6 files changed

+89
-0
lines changed

6 files changed

+89
-0
lines changed

src/math/big/example_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ func ExampleInt_Scan() {
5151
// Output: 18446744073709551617
5252
}
5353

54+
func ExampleFloat_Scan() {
55+
// The Scan function is rarely used directly;
56+
// the fmt package recognizes it as an implementation of fmt.Scanner.
57+
f := new(big.Float)
58+
_, err := fmt.Sscan("1.19282e99", f)
59+
if err != nil {
60+
log.Println("error scanning value:", err)
61+
} else {
62+
fmt.Println(f)
63+
}
64+
// Output: 1.19282e+99
65+
}
66+
5467
// This example demonstrates how to use big.Int to compute the smallest
5568
// Fibonacci number with 100 decimal digits and to test whether it is prime.
5669
func Example_fibonacci() {

src/math/big/floatconv.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"strings"
1313
)
1414

15+
var floatZero Float
16+
1517
// SetString sets z to the value of s and returns z and a boolean indicating
1618
// success. s must be a floating-point number of the same format as accepted
1719
// by Parse, with base argument 0. The entire string (not just a prefix) must
@@ -276,3 +278,16 @@ func (z *Float) Parse(s string, base int) (f *Float, b int, err error) {
276278
func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
277279
return new(Float).SetPrec(prec).SetMode(mode).Parse(s, base)
278280
}
281+
282+
var _ fmt.Scanner = &floatZero // *Float must implement fmt.Scanner
283+
284+
// Scan is a support routine for fmt.Scanner; it sets z to the value of
285+
// the scanned number. It accepts formats whose verbs are supported by
286+
// fmt.Scan for floating point values, which are:
287+
// 'b' (binary), 'e', 'E', 'f', 'F', 'g' and 'G'.
288+
// Scan doesn't handle ±Inf.
289+
func (z *Float) Scan(s fmt.ScanState, ch rune) error {
290+
s.SkipSpace()
291+
_, _, err := z.scan(byteReader{s}, 0)
292+
return err
293+
}

src/math/big/floatconv_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package big
66

77
import (
8+
"bytes"
89
"fmt"
910
"math"
1011
"strconv"
@@ -665,3 +666,54 @@ func BenchmarkParseFloatLargeExp(b *testing.B) {
665666
}
666667
}
667668
}
669+
670+
func TestFloatScan(t *testing.T) {
671+
var floatScanTests = []struct {
672+
input string
673+
format string
674+
output string
675+
remaining int
676+
wantErr bool
677+
}{
678+
0: {"10.0", "%f", "10", 0, false},
679+
1: {"23.98+2.0", "%v", "23.98", 4, false},
680+
2: {"-1+1", "%v", "-1", 2, false},
681+
3: {" 00000", "%v", "0", 0, false},
682+
4: {"-123456p-78", "%b", "-4.084816388e-19", 0, false},
683+
5: {"+123", "%b", "123", 0, false},
684+
6: {"-1.234e+56", "%e", "-1.234e+56", 0, false},
685+
7: {"-1.234E-56", "%E", "-1.234e-56", 0, false},
686+
8: {"-1.234e+567", "%g", "-1.234e+567", 0, false},
687+
9: {"+1234567891011.234", "%G", "1.234567891e+12", 0, false},
688+
689+
// Scan doesn't handle ±Inf.
690+
10: {"Inf", "%v", "", 3, true},
691+
11: {"-Inf", "%v", "", 3, true},
692+
12: {"-Inf", "%v", "", 3, true},
693+
}
694+
695+
var buf bytes.Buffer
696+
for i, test := range floatScanTests {
697+
x := new(Float)
698+
buf.Reset()
699+
buf.WriteString(test.input)
700+
_, err := fmt.Fscanf(&buf, test.format, x)
701+
if test.wantErr {
702+
if err == nil {
703+
t.Errorf("#%d want non-nil err", i)
704+
}
705+
continue
706+
}
707+
708+
if err != nil {
709+
t.Errorf("#%d error: %s", i, err)
710+
}
711+
712+
if x.String() != test.output {
713+
t.Errorf("#%d got %s; want %s", i, x.String(), test.output)
714+
}
715+
if buf.Len() != test.remaining {
716+
t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining)
717+
}
718+
}
719+
}

src/math/big/ftoa.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,8 @@ func min(x, y int) int {
376376
return y
377377
}
378378

379+
var _ fmt.Formatter = &floatZero // *Float must implement fmt.Formatter
380+
379381
// Format implements fmt.Formatter. It accepts all the regular
380382
// formats for floating-point numbers ('b', 'e', 'E', 'f', 'F',
381383
// 'g', 'G') as well as 'p' and 'v'. See (*Float).Text for the

src/math/big/intconv.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ func writeMultiple(s fmt.State, text string, count int) {
5252
}
5353
}
5454

55+
var _ fmt.Formatter = intOne // *Int must implement fmt.Formatter
56+
5557
// Format implements fmt.Formatter. It accepts the formats
5658
// 'b' (binary), 'o' (octal), 'd' (decimal), 'x' (lowercase
5759
// hexadecimal), and 'X' (uppercase hexadecimal).
@@ -223,6 +225,8 @@ func (r byteReader) UnreadByte() error {
223225
return r.UnreadRune()
224226
}
225227

228+
var _ fmt.Scanner = intOne // *Int must implement fmt.Scanner
229+
226230
// Scan is a support routine for fmt.Scanner; it sets z to the value of
227231
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
228232
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).

src/math/big/ratconv.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ func ratTok(ch rune) bool {
1818
return strings.ContainsRune("+-/0123456789.eE", ch)
1919
}
2020

21+
var ratZero Rat
22+
var _ fmt.Scanner = &ratZero // *Rat must implement fmt.Scanner
23+
2124
// Scan is a support routine for fmt.Scanner. It accepts the formats
2225
// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent.
2326
func (z *Rat) Scan(s fmt.ScanState, ch rune) error {

0 commit comments

Comments
 (0)