1// Copyright 2010 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 runtime_test
6
7import (
8	"math"
9	"math/rand"
10	. "runtime"
11	"testing"
12)
13
14// turn uint64 op into float64 op
15func fop(f func(x, y uint64) uint64) func(x, y float64) float64 {
16	return func(x, y float64) float64 {
17		bx := math.Float64bits(x)
18		by := math.Float64bits(y)
19		return math.Float64frombits(f(bx, by))
20	}
21}
22
23func add(x, y float64) float64 { return x + y }
24func sub(x, y float64) float64 { return x - y }
25func mul(x, y float64) float64 { return x * y }
26func div(x, y float64) float64 { return x / y }
27
28func TestFloat64(t *testing.T) {
29	base := []float64{
30		0,
31		math.Copysign(0, -1),
32		-1,
33		1,
34		math.NaN(),
35		math.Inf(+1),
36		math.Inf(-1),
37		0.1,
38		1.5,
39		1.9999999999999998,     // all 1s mantissa
40		1.3333333333333333,     // 1.010101010101...
41		1.1428571428571428,     // 1.001001001001...
42		1.112536929253601e-308, // first normal
43		2,
44		4,
45		8,
46		16,
47		32,
48		64,
49		128,
50		256,
51		3,
52		12,
53		1234,
54		123456,
55		-0.1,
56		-1.5,
57		-1.9999999999999998,
58		-1.3333333333333333,
59		-1.1428571428571428,
60		-2,
61		-3,
62		1e-200,
63		1e-300,
64		1e-310,
65		5e-324,
66		1e-105,
67		1e-305,
68		1e+200,
69		1e+306,
70		1e+307,
71		1e+308,
72	}
73	all := make([]float64, 200)
74	copy(all, base)
75	for i := len(base); i < len(all); i++ {
76		all[i] = rand.NormFloat64()
77	}
78
79	test(t, "+", add, fop(Fadd64), all)
80	test(t, "-", sub, fop(Fsub64), all)
81	if GOARCH != "386" { // 386 is not precise!
82		test(t, "*", mul, fop(Fmul64), all)
83		test(t, "/", div, fop(Fdiv64), all)
84	}
85}
86
87// 64 -hw-> 32 -hw-> 64
88func trunc32(f float64) float64 {
89	return float64(float32(f))
90}
91
92// 64 -sw->32 -hw-> 64
93func to32sw(f float64) float64 {
94	return float64(math.Float32frombits(F64to32(math.Float64bits(f))))
95}
96
97// 64 -hw->32 -sw-> 64
98func to64sw(f float64) float64 {
99	return math.Float64frombits(F32to64(math.Float32bits(float32(f))))
100}
101
102// float64 -hw-> int64 -hw-> float64
103func hwint64(f float64) float64 {
104	return float64(int64(f))
105}
106
107// float64 -hw-> int32 -hw-> float64
108func hwint32(f float64) float64 {
109	return float64(int32(f))
110}
111
112// float64 -sw-> int64 -hw-> float64
113func toint64sw(f float64) float64 {
114	i, ok := F64toint(math.Float64bits(f))
115	if !ok {
116		// There's no right answer for out of range.
117		// Match the hardware to pass the test.
118		i = int64(f)
119	}
120	return float64(i)
121}
122
123// float64 -hw-> int64 -sw-> float64
124func fromint64sw(f float64) float64 {
125	return math.Float64frombits(Fintto64(int64(f)))
126}
127
128var nerr int
129
130func err(t *testing.T, format string, args ...any) {
131	t.Errorf(format, args...)
132
133	// cut errors off after a while.
134	// otherwise we spend all our time
135	// allocating memory to hold the
136	// formatted output.
137	if nerr++; nerr >= 10 {
138		t.Fatal("too many errors")
139	}
140}
141
142func test(t *testing.T, op string, hw, sw func(float64, float64) float64, all []float64) {
143	for _, f := range all {
144		for _, g := range all {
145			h := hw(f, g)
146			s := sw(f, g)
147			if !same(h, s) {
148				err(t, "%g %s %g = sw %g, hw %g\n", f, op, g, s, h)
149			}
150			testu(t, "to32", trunc32, to32sw, h)
151			testu(t, "to64", trunc32, to64sw, h)
152			testu(t, "toint64", hwint64, toint64sw, h)
153			testu(t, "fromint64", hwint64, fromint64sw, h)
154			testcmp(t, f, h)
155			testcmp(t, h, f)
156			testcmp(t, g, h)
157			testcmp(t, h, g)
158		}
159	}
160}
161
162func testu(t *testing.T, op string, hw, sw func(float64) float64, v float64) {
163	h := hw(v)
164	s := sw(v)
165	if !same(h, s) {
166		err(t, "%s %g = sw %g, hw %g\n", op, v, s, h)
167	}
168}
169
170func hwcmp(f, g float64) (cmp int, isnan bool) {
171	switch {
172	case f < g:
173		return -1, false
174	case f > g:
175		return +1, false
176	case f == g:
177		return 0, false
178	}
179	return 0, true // must be NaN
180}
181
182func testcmp(t *testing.T, f, g float64) {
183	hcmp, hisnan := hwcmp(f, g)
184	scmp, sisnan := Fcmp64(math.Float64bits(f), math.Float64bits(g))
185	if int32(hcmp) != scmp || hisnan != sisnan {
186		err(t, "cmp(%g, %g) = sw %v, %v, hw %v, %v\n", f, g, scmp, sisnan, hcmp, hisnan)
187	}
188}
189
190func same(f, g float64) bool {
191	if math.IsNaN(f) && math.IsNaN(g) {
192		return true
193	}
194	if math.Copysign(1, f) != math.Copysign(1, g) {
195		return false
196	}
197	return f == g
198}
199