1
2
3
4
5 package strconv_test
6
7 import (
8 _ "embed"
9 "flag"
10 "fmt"
11 . "internal/strconv"
12 "io"
13 "net/http"
14 "os"
15 "strings"
16 "testing"
17 )
18
19 func pow2(i int) float64 {
20 switch {
21 case i < 0:
22 return 1 / pow2(-i)
23 case i == 0:
24 return 1
25 case i == 1:
26 return 2
27 }
28 return pow2(i/2) * pow2(i-i/2)
29 }
30
31
32
33 func myatof64(s string) (f float64, ok bool) {
34 if mant, exp, ok := strings.Cut(s, "p"); ok {
35 n, err := ParseInt(mant, 10, 64)
36 if err != nil {
37 return 0, false
38 }
39 e, err1 := Atoi(exp)
40 if err1 != nil {
41 println("bad e", exp)
42 return 0, false
43 }
44 v := float64(n)
45
46
47 if e <= -1000 {
48 v *= pow2(-1000)
49 e += 1000
50 for e < 0 {
51 v /= 2
52 e++
53 }
54 return v, true
55 }
56 if e >= 1000 {
57 v *= pow2(1000)
58 e -= 1000
59 for e > 0 {
60 v *= 2
61 e--
62 }
63 return v, true
64 }
65 return v * pow2(e), true
66 }
67 f1, err := ParseFloat(s, 64)
68 if err != nil {
69 return 0, false
70 }
71 return f1, true
72 }
73
74
75
76 func myatof32(s string) (f float32, ok bool) {
77 if mant, exp, ok := strings.Cut(s, "p"); ok {
78 n, err := Atoi(mant)
79 if err != nil {
80 println("bad n", mant)
81 return 0, false
82 }
83 e, err1 := Atoi(exp)
84 if err1 != nil {
85 println("bad p", exp)
86 return 0, false
87 }
88 return float32(float64(n) * pow2(e)), true
89 }
90 f64, err1 := ParseFloat(s, 32)
91 f1 := float32(f64)
92 if err1 != nil {
93 return 0, false
94 }
95 return f1, true
96 }
97
98
99 var testfp string
100
101 func TestFp(t *testing.T) {
102 lineno := 0
103 for line := range strings.Lines(testfp) {
104 lineno++
105 line, _, _ = strings.Cut(line, "#")
106 line = strings.TrimSpace(line)
107 if line == "" {
108 continue
109 }
110 a := strings.Split(line, " ")
111 if len(a) != 4 {
112 t.Errorf("testdata/testfp.txt:%d: wrong field count", lineno)
113 continue
114 }
115 var s string
116 var v float64
117 switch a[0] {
118 case "float64":
119 var ok bool
120 v, ok = myatof64(a[2])
121 if !ok {
122 t.Errorf("testdata/testfp.txt:%d: cannot atof64 %s", lineno, a[2])
123 continue
124 }
125 s = fmt.Sprintf(a[1], v)
126 case "float32":
127 v1, ok := myatof32(a[2])
128 if !ok {
129 t.Errorf("testdata/testfp.txt:%d: cannot atof32 %s", lineno, a[2])
130 continue
131 }
132 s = fmt.Sprintf(a[1], v1)
133 v = float64(v1)
134 }
135 if s != a[3] {
136 t.Errorf("testdata/testfp.txt:%d: %s %s %s %s: have %s want %s", lineno, a[0], a[1], a[2], a[3], s, a[3])
137 }
138 }
139 }
140
141
142
143 var testbase = flag.Bool("testbase", false, "download and test full testbase testdata")
144
145
146
147 var testbaseURL = "https://gist.githubusercontent.com/rsc/606b378b0bf95c24a6fd6cef99e262e1/raw/128a03890e536bdf403e6cc768b0737405c6734d/"
148
149
150 var atof1ktxt string
151
152
153 var ftoa1ktxt string
154
155
156
157
158
159
160 func openTestbase(t *testing.T, name string) (file, data string) {
161 if !*testbase {
162 switch name {
163 case "atof":
164 return "testdata/atof1k.txt", atof1ktxt
165 case "ftoa":
166 return "testdata/ftoa1k.txt", ftoa1ktxt
167 }
168 t.Fatalf("unknown file %s", name)
169 }
170
171
172 file = "testdata/" + name + ".txt"
173 if data, err := os.ReadFile(file); err == nil {
174 return file, string(data)
175 }
176
177
178 url := testbaseURL + name + ".txt"
179 resp, err := http.Get(url)
180 if err != nil {
181 t.Fatalf("%s: %s", url, err)
182 }
183 if resp.StatusCode != 200 {
184 t.Fatalf("%s: %s", url, resp.Status)
185 }
186 bytes, err := io.ReadAll(resp.Body)
187 resp.Body.Close()
188 if err != nil {
189 t.Fatalf("%s: %s", url, err)
190 }
191 if err := os.WriteFile(file, bytes, 0666); err != nil {
192 t.Fatal(err)
193 }
194 return file, string(bytes)
195 }
196
197 func TestParseFloatTestdata(t *testing.T) {
198
199 name, data := openTestbase(t, "atof")
200 fail := 0
201 lineno := 0
202 for line := range strings.Lines(data) {
203 lineno++
204 s := strings.TrimSpace(line)
205 if strings.HasPrefix(s, "#") || s == "" {
206 continue
207 }
208 SetOptimize(false)
209 want, err1 := ParseFloat(s, 64)
210 SetOptimize(true)
211 have, err2 := ParseFloat(s, 64)
212 if err1 != nil {
213
214 t.Errorf("%s:%d: ParseFloat(%#q): %v", name, lineno, s, err1)
215 continue
216 }
217 if err2 != nil {
218 t.Errorf("ParseFloat(%#q): %v", s, err2)
219 if fail++; fail > 100 {
220 t.Fatalf("too many failures")
221 }
222 continue
223 }
224 if have != want {
225 t.Errorf("ParseFloat(%#q) = %#x, want %#x", s, have, want)
226 if fail++; fail > 100 {
227 t.Fatalf("too many failures")
228 }
229 }
230 }
231 }
232
233 func TestFormatFloatTestdata(t *testing.T) {
234
235 name, data := openTestbase(t, "ftoa")
236 fail := 0
237 lineno := 0
238 for line := range strings.Lines(data) {
239 lineno++
240 s := strings.TrimSpace(line)
241 if strings.HasPrefix(s, "#") || s == "" {
242 continue
243 }
244 f, err := ParseFloat(s, 64)
245 if err != nil {
246
247 t.Errorf("%s:%d: ParseFloat(%#q): %v", name, lineno, s, err)
248 continue
249 }
250 for i := range 19 {
251 SetOptimize(false)
252 want := FormatFloat(f, 'e', i, 64)
253 SetOptimize(true)
254 have := FormatFloat(f, 'e', i, 64)
255 if have != want {
256 t.Errorf("FormatFloat(%#x, 'e', %d) = %s, want %s", f, i, have, want)
257 if fail++; fail > 100 {
258 t.Fatalf("too many failures")
259 }
260 }
261 }
262 }
263 }
264
View as plain text