1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package strconv_test
6
7import (
8	"bufio"
9	"fmt"
10	"os"
11	"strconv"
12	"strings"
13	"testing"
14)
15
16func pow2(i int) float64 {
17	switch {
18	case i < 0:
19		return 1 / pow2(-i)
20	case i == 0:
21		return 1
22	case i == 1:
23		return 2
24	}
25	return pow2(i/2) * pow2(i-i/2)
26}
27
28// Wrapper around strconv.ParseFloat(x, 64).  Handles dddddp+ddd (binary exponent)
29// itself, passes the rest on to strconv.ParseFloat.
30func myatof64(s string) (f float64, ok bool) {
31	a := strings.SplitN(s, "p", 2)
32	if len(a) == 2 {
33		n, err := strconv.ParseInt(a[0], 10, 64)
34		if err != nil {
35			return 0, false
36		}
37		e, err1 := strconv.Atoi(a[1])
38		if err1 != nil {
39			println("bad e", a[1])
40			return 0, false
41		}
42		v := float64(n)
43		// We expect that v*pow2(e) fits in a float64,
44		// but pow2(e) by itself may not. Be careful.
45		if e <= -1000 {
46			v *= pow2(-1000)
47			e += 1000
48			for e < 0 {
49				v /= 2
50				e++
51			}
52			return v, true
53		}
54		if e >= 1000 {
55			v *= pow2(1000)
56			e -= 1000
57			for e > 0 {
58				v *= 2
59				e--
60			}
61			return v, true
62		}
63		return v * pow2(e), true
64	}
65	f1, err := strconv.ParseFloat(s, 64)
66	if err != nil {
67		return 0, false
68	}
69	return f1, true
70}
71
72// Wrapper around strconv.ParseFloat(x, 32).  Handles dddddp+ddd (binary exponent)
73// itself, passes the rest on to strconv.ParseFloat.
74func myatof32(s string) (f float32, ok bool) {
75	a := strings.SplitN(s, "p", 2)
76	if len(a) == 2 {
77		n, err := strconv.Atoi(a[0])
78		if err != nil {
79			println("bad n", a[0])
80			return 0, false
81		}
82		e, err1 := strconv.Atoi(a[1])
83		if err1 != nil {
84			println("bad p", a[1])
85			return 0, false
86		}
87		return float32(float64(n) * pow2(e)), true
88	}
89	f64, err1 := strconv.ParseFloat(s, 32)
90	f1 := float32(f64)
91	if err1 != nil {
92		return 0, false
93	}
94	return f1, true
95}
96
97func TestFp(t *testing.T) {
98	f, err := os.Open("testdata/testfp.txt")
99	if err != nil {
100		t.Fatal("testfp: open testdata/testfp.txt:", err)
101	}
102	defer f.Close()
103
104	s := bufio.NewScanner(f)
105
106	for lineno := 1; s.Scan(); lineno++ {
107		line := s.Text()
108		if len(line) == 0 || line[0] == '#' {
109			continue
110		}
111		a := strings.Split(line, " ")
112		if len(a) != 4 {
113			t.Error("testdata/testfp.txt:", lineno, ": wrong field count")
114			continue
115		}
116		var s string
117		var v float64
118		switch a[0] {
119		case "float64":
120			var ok bool
121			v, ok = myatof64(a[2])
122			if !ok {
123				t.Error("testdata/testfp.txt:", lineno, ": cannot atof64 ", a[2])
124				continue
125			}
126			s = fmt.Sprintf(a[1], v)
127		case "float32":
128			v1, ok := myatof32(a[2])
129			if !ok {
130				t.Error("testdata/testfp.txt:", lineno, ": cannot atof32 ", a[2])
131				continue
132			}
133			s = fmt.Sprintf(a[1], v1)
134			v = float64(v1)
135		}
136		if s != a[3] {
137			t.Error("testdata/testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ",
138				"want ", a[3], " got ", s)
139		}
140	}
141	if s.Err() != nil {
142		t.Fatal("testfp: read testdata/testfp.txt: ", s.Err())
143	}
144}
145