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	if mant, exp, ok := strings.Cut(s, "p"); ok {
32		n, err := strconv.ParseInt(mant, 10, 64)
33		if err != nil {
34			return 0, false
35		}
36		e, err1 := strconv.Atoi(exp)
37		if err1 != nil {
38			println("bad e", exp)
39			return 0, false
40		}
41		v := float64(n)
42		// We expect that v*pow2(e) fits in a float64,
43		// but pow2(e) by itself may not. Be careful.
44		if e <= -1000 {
45			v *= pow2(-1000)
46			e += 1000
47			for e < 0 {
48				v /= 2
49				e++
50			}
51			return v, true
52		}
53		if e >= 1000 {
54			v *= pow2(1000)
55			e -= 1000
56			for e > 0 {
57				v *= 2
58				e--
59			}
60			return v, true
61		}
62		return v * pow2(e), true
63	}
64	f1, err := strconv.ParseFloat(s, 64)
65	if err != nil {
66		return 0, false
67	}
68	return f1, true
69}
70
71// Wrapper around strconv.ParseFloat(x, 32).  Handles dddddp+ddd (binary exponent)
72// itself, passes the rest on to strconv.ParseFloat.
73func myatof32(s string) (f float32, ok bool) {
74	if mant, exp, ok := strings.Cut(s, "p"); ok {
75		n, err := strconv.Atoi(mant)
76		if err != nil {
77			println("bad n", mant)
78			return 0, false
79		}
80		e, err1 := strconv.Atoi(exp)
81		if err1 != nil {
82			println("bad p", exp)
83			return 0, false
84		}
85		return float32(float64(n) * pow2(e)), true
86	}
87	f64, err1 := strconv.ParseFloat(s, 32)
88	f1 := float32(f64)
89	if err1 != nil {
90		return 0, false
91	}
92	return f1, true
93}
94
95func TestFp(t *testing.T) {
96	f, err := os.Open("testdata/testfp.txt")
97	if err != nil {
98		t.Fatal("testfp: open testdata/testfp.txt:", err)
99	}
100	defer f.Close()
101
102	s := bufio.NewScanner(f)
103
104	for lineno := 1; s.Scan(); lineno++ {
105		line := s.Text()
106		if len(line) == 0 || line[0] == '#' {
107			continue
108		}
109		a := strings.Split(line, " ")
110		if len(a) != 4 {
111			t.Error("testdata/testfp.txt:", lineno, ": wrong field count")
112			continue
113		}
114		var s string
115		var v float64
116		switch a[0] {
117		case "float64":
118			var ok bool
119			v, ok = myatof64(a[2])
120			if !ok {
121				t.Error("testdata/testfp.txt:", lineno, ": cannot atof64 ", a[2])
122				continue
123			}
124			s = fmt.Sprintf(a[1], v)
125		case "float32":
126			v1, ok := myatof32(a[2])
127			if !ok {
128				t.Error("testdata/testfp.txt:", lineno, ": cannot atof32 ", a[2])
129				continue
130			}
131			s = fmt.Sprintf(a[1], v1)
132			v = float64(v1)
133		}
134		if s != a[3] {
135			t.Error("testdata/testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ",
136				"want ", a[3], " got ", s)
137		}
138	}
139	if s.Err() != nil {
140		t.Fatal("testfp: read testdata/testfp.txt: ", s.Err())
141	}
142}
143