1
2
3
4
5 package spec
6
7 import (
8 "math"
9 "testing"
10 )
11
12 func f32(bits uint32) float32 { return math.Float32frombits(bits) }
13 func f64(bits uint64) float64 { return math.Float64frombits(bits) }
14
15
16 var (
17 negZero32 = f32(0x80000000)
18 minSub32 = f32(0x00000001)
19 maxSub32 = f32(0x007fffff)
20 minNorm32 = f32(0x00800000)
21 inf32 = f32(0x7f800000)
22 qnan32 = f32(0x7fc00000)
23 qnanPay32 = f32(0x7fc00123)
24 snan32 = f32(0x7f800001)
25 negQnan32 = f32(0xffc00000)
26 maxFloat32 = float32(math.MaxFloat32)
27 )
28
29
30 var (
31 negZero64 = f64(0x8000000000000000)
32 minSub64 = f64(0x0000000000000001)
33 maxSub64 = f64(0x000fffffffffffff)
34 minNorm64 = f64(0x0010000000000000)
35 inf64 = math.Inf(1)
36 qnan64 = f64(0x7ff8000000000000)
37 qnanPay64 = f64(0x7ff8000000000123)
38 snan64 = f64(0x7ff0000000000001)
39 negQnan64 = f64(0xfff8000000000000)
40 )
41
42
43 func allPlatforms() []Platform {
44 var ps []Platform
45 for p := range numPlatforms {
46 ps = append(ps, p)
47 }
48 return append(ps, PlatformAny)
49 }
50
51
52
53 func checkPermits[E EltOrMask](t *testing.T, v Variation[E], plat Platform, yes, no []E) {
54 t.Helper()
55 for _, x := range yes {
56 if !v.Permits(x, plat) {
57 t.Errorf("%v on %v: %v not permitted, want permitted", v, plat, x)
58 }
59 }
60 for _, x := range no {
61 if v.Permits(x, plat) {
62 t.Errorf("%v on %v: %v permitted, want not permitted", v, plat, x)
63 }
64 }
65 }
66
67
68 func checkPermitsAll[E EltOrMask](t *testing.T, v Variation[E], yes, no []E) {
69 t.Helper()
70 for _, p := range allPlatforms() {
71 checkPermits(t, v, p, yes, no)
72 }
73 }
74
75 func checkCanon[E EltOrMask](t *testing.T, v Variation[E], want E) {
76 t.Helper()
77 if got := v.Canon(); !equalExact(got, want) {
78 t.Errorf("%v: Canon() = %v, want %v", v, got, want)
79 }
80 }
81
82 func checkString[E EltOrMask](t *testing.T, v Variation[E], want string) {
83 t.Helper()
84 if got := v.String(); got != want {
85 t.Errorf("String() = %q, want %q", got, want)
86 }
87 }
88
89 func mustPanic(t *testing.T, name string, f func()) {
90 t.Helper()
91 defer func() {
92 if recover() == nil {
93 t.Errorf("%s did not panic", name)
94 }
95 }()
96 f()
97 }
98
99 type equalExactCase[E EltOrMask] struct {
100 a, b E
101 want bool
102 }
103
104 func checkEqualExact[E EltOrMask](t *testing.T, cases []equalExactCase[E]) {
105 t.Helper()
106 for _, c := range cases {
107
108 if got := equalExact(c.a, c.b); got != c.want {
109 t.Errorf("equalExact(%v, %v) = %v, want %v", c.a, c.b, got, c.want)
110 }
111 if got := equalExact(c.b, c.a); got != c.want {
112 t.Errorf("equalExact(%v, %v) = %v, want %v", c.b, c.a, got, c.want)
113 }
114 }
115 }
116
117 func TestEqualExact(t *testing.T) {
118 checkEqualExact(t, []equalExactCase[int8]{
119 {-1, -1, true},
120 {-1, 1, false},
121 {math.MinInt8, math.MinInt8, true},
122 {math.MinInt8, math.MaxInt8, false},
123 })
124 checkEqualExact(t, []equalExactCase[uint64]{
125 {math.MaxUint64, math.MaxUint64, true},
126 {math.MaxUint64, math.MaxUint64 - 1, false},
127 })
128 checkEqualExact(t, []equalExactCase[Mask32]{
129 {^Mask32(0), ^Mask32(0), true},
130 {0, 0, true},
131 {0, ^Mask32(0), false},
132 })
133
134 checkEqualExact(t, []equalExactCase[float32]{
135 {1, 1, true},
136 {1, math.Nextafter32(1, 2), false},
137
138 {0, negZero32, false},
139 {negZero32, negZero32, true},
140
141 {minSub32, minSub32, true},
142 {minSub32, 0, false},
143 {maxSub32, minNorm32, false},
144 {inf32, inf32, true},
145 {inf32, -inf32, false},
146 {inf32, maxFloat32, false},
147
148
149 {qnan32, qnan32, true},
150 {qnan32, qnanPay32, true},
151 {qnan32, snan32, true},
152 {qnan32, negQnan32, true},
153 {snan32, negQnan32, true},
154 {qnan32, 0, false},
155 {qnan32, inf32, false},
156 {snan32, inf32, false},
157 })
158
159 checkEqualExact(t, []equalExactCase[float64]{
160 {1, 1, true},
161 {1, math.Nextafter(1, 2), false},
162 {0, negZero64, false},
163 {negZero64, negZero64, true},
164 {minSub64, minSub64, true},
165 {minSub64, 0, false},
166 {maxSub64, minNorm64, false},
167 {inf64, inf64, true},
168 {inf64, -inf64, false},
169 {inf64, math.MaxFloat64, false},
170 {qnan64, qnan64, true},
171 {qnan64, qnanPay64, true},
172 {qnan64, snan64, true},
173 {qnan64, negQnan64, true},
174 {snan64, negQnan64, true},
175 {qnan64, 0, false},
176 {qnan64, inf64, false},
177 {snan64, inf64, false},
178 })
179 }
180
181 func TestExact(t *testing.T) {
182 v := exact[int32](5)
183 checkCanon(t, v, 5)
184 checkString(t, v, "5")
185 checkPermitsAll(t, v, []int32{5}, []int32{4, 6, 0})
186
187
188 checkPermitsAll(t, exact(qnan32), []float32{qnan32, snan32, negQnan32}, []float32{0, inf32})
189 checkPermitsAll(t, exact(float32(0)), []float32{0}, []float32{negZero32})
190
191
192 var zero Variation[int16]
193 checkCanon(t, zero, 0)
194 checkPermitsAll(t, zero, []int16{0}, []int16{1, -1})
195 }
196
197 func TestPerPlatform(t *testing.T) {
198 v := perPlatform(map[Platform]int32{
199 PlatformARM64: 2,
200 PlatformWASM: 3,
201 })
202
203 checkCanon(t, v, 2)
204 checkString(t, v, "{arm64:2 wasm:3}")
205
206 checkPermits(t, v, PlatformARM64, []int32{2}, []int32{3, 0})
207 checkPermits(t, v, PlatformWASM, []int32{3}, []int32{2, 0})
208
209 checkPermits(t, v, PlatformAMD64, nil, []int32{2, 3, 0})
210
211 checkPermits(t, v, PlatformAny, []int32{2, 3}, []int32{0})
212
213 checkPermits(t, v, numPlatforms, nil, []int32{2, 3})
214 checkPermits(t, v, Platform(-2), nil, []int32{2, 3})
215
216
217 vf := perPlatform(map[Platform]float32{
218 PlatformAMD64: qnan32,
219 PlatformARM64: 0,
220 })
221 checkPermits(t, vf, PlatformAMD64, []float32{snan32}, []float32{0})
222 checkPermits(t, vf, PlatformARM64, []float32{0}, []float32{negZero32, qnan32})
223
224 mustPanic(t, "perPlatform(empty)", func() { perPlatform(map[Platform]int32{}) })
225 mustPanic(t, "perPlatform(no recognized platforms)", func() {
226 perPlatform(map[Platform]int32{PlatformAny: 1})
227 })
228 }
229
230 func TestAnyOf(t *testing.T) {
231
232 v1 := anyOf[int32](7)
233 checkCanon(t, v1, 7)
234 checkString(t, v1, "7")
235 checkPermitsAll(t, v1, []int32{7}, []int32{6, 8})
236
237 v := anyOf[int32](1, 2, 3)
238
239 checkCanon(t, v, 1)
240 checkString(t, v, "{1|2|3}")
241 checkPermitsAll(t, v, []int32{1, 2, 3}, []int32{0, 4})
242
243
244 checkPermitsAll(t, anyOf(float32(0), qnan32), []float32{0, snan32}, []float32{negZero32, 1})
245
246 mustPanic(t, "anyOf()", func() { anyOf[int32]() })
247 }
248
249 func TestUndef(t *testing.T) {
250 v := undef[int8]()
251 checkCanon(t, v, 0)
252 checkString(t, v, "undef")
253 checkPermitsAll(t, v, []int8{0, 1, -1, math.MinInt8, math.MaxInt8}, nil)
254
255 checkPermitsAll(t, undef[float32](), []float32{0, negZero32, inf32, -inf32, qnan32, snan32}, nil)
256 }
257
258 func TestULPs(t *testing.T) {
259 t.Run("float32", func(t *testing.T) {
260
261
262 up := func(x float32, n int32) float32 {
263 return f32(uint32(int32(math.Float32bits(x)) + n))
264 }
265
266
267 v0 := ulps(float32(1), 0)
268 checkString(t, v0, "1")
269 checkPermitsAll(t, v0, []float32{1}, []float32{up(1, 1), up(1, -1)})
270
271 v := ulps(float32(1), 2)
272 checkCanon(t, v, 1)
273 checkString(t, v, "1±2ulp")
274
275
276 checkPermitsAll(t, v,
277 []float32{1, up(1, 1), up(1, 2), up(1, -1), up(1, -2)},
278 []float32{up(1, 3), up(1, -3), -1, 0})
279
280
281
282 checkPermitsAll(t, ulps(float32(2), 1), []float32{up(2, -1)}, []float32{up(2, -2)})
283
284
285 checkPermitsAll(t, ulps(minNorm32, 1), []float32{maxSub32}, []float32{up(maxSub32, -1)})
286 checkPermitsAll(t, ulps(minSub32, 1), []float32{0, up(minSub32, 1)}, []float32{up(minSub32, 2)})
287
288
289
290
291 checkPermitsAll(t, ulps(float32(0), 1), []float32{0, minSub32}, []float32{negZero32, -minSub32})
292 checkPermitsAll(t, ulps(minSub32, 1), nil, []float32{negZero32, -minSub32})
293 checkPermitsAll(t, ulps(negZero32, 1), []float32{negZero32, -minSub32}, []float32{0, minSub32})
294
295
296 checkPermitsAll(t, ulps(float32(-1), 1),
297 []float32{-1, up(-1, 1), up(-1, -1)},
298 []float32{up(-1, 2), 1})
299
300
301
302 checkPermitsAll(t, ulps(maxFloat32, 1), []float32{up(maxFloat32, -1)}, []float32{inf32})
303 checkPermitsAll(t, ulps(inf32, 1), []float32{inf32}, []float32{maxFloat32, -inf32, qnan32})
304 checkPermitsAll(t, ulps(-inf32, 1), []float32{-inf32}, []float32{-maxFloat32, inf32})
305
306
307
308 checkPermitsAll(t, ulps(qnan32, 1), []float32{qnan32, snan32, negQnan32}, []float32{0, inf32})
309 checkPermitsAll(t, ulps(float32(1), 1<<30), nil, []float32{qnan32, snan32, negQnan32})
310 checkPermitsAll(t, ulps(maxFloat32, 1<<30), nil, []float32{snan32})
311
312
313 checkPermitsAll(t, ulps(float32(1), 1<<23), []float32{2}, []float32{up(2, 1)})
314
315
316
317
318 checkPermitsAll(t, ulps(minSub32, 1<<31), []float32{maxFloat32}, []float32{negZero32, -minSub32})
319 checkPermitsAll(t, ulps(float32(1), 1<<32), []float32{0, 2, maxFloat32}, []float32{-1, inf32, qnan32})
320 checkPermitsAll(t, ulps(float32(1), math.MaxUint64), []float32{0, minSub32, maxFloat32}, []float32{negZero32, -1, inf32, qnan32})
321 })
322
323 t.Run("float64", func(t *testing.T) {
324 up := func(x float64, n int64) float64 {
325 return f64(uint64(int64(math.Float64bits(x)) + n))
326 }
327
328 checkPermitsAll(t, ulps(1.0, 0), []float64{1}, []float64{up(1, 1)})
329
330 v := ulps(1.0, 2)
331 checkCanon(t, v, 1)
332 checkString(t, v, "1±2ulp")
333 checkPermitsAll(t, v,
334 []float64{1, up(1, 1), up(1, 2), up(1, -1), up(1, -2)},
335 []float64{up(1, 3), up(1, -3), -1, 0})
336
337 checkPermitsAll(t, ulps(2.0, 1), []float64{up(2, -1)}, []float64{up(2, -2)})
338
339 checkPermitsAll(t, ulps(minNorm64, 1), []float64{maxSub64}, []float64{up(maxSub64, -1)})
340 checkPermitsAll(t, ulps(minSub64, 1), []float64{0, up(minSub64, 1)}, []float64{up(minSub64, 2)})
341
342 checkPermitsAll(t, ulps(0.0, 1), []float64{0, minSub64}, []float64{negZero64, -minSub64})
343 checkPermitsAll(t, ulps(negZero64, 1), []float64{negZero64, -minSub64}, []float64{0, minSub64})
344
345 checkPermitsAll(t, ulps(-1.0, 1),
346 []float64{-1, up(-1, 1), up(-1, -1)},
347 []float64{up(-1, 2), 1})
348
349 checkPermitsAll(t, ulps(math.MaxFloat64, 1), []float64{up(math.MaxFloat64, -1)}, []float64{inf64})
350 checkPermitsAll(t, ulps(inf64, 1), []float64{inf64}, []float64{math.MaxFloat64, -inf64, qnan64})
351 checkPermitsAll(t, ulps(-inf64, 1), []float64{-inf64}, []float64{-math.MaxFloat64, inf64})
352
353 checkPermitsAll(t, ulps(qnan64, 1), []float64{qnan64, snan64, negQnan64}, []float64{0, inf64})
354 checkPermitsAll(t, ulps(1.0, 1<<30), nil, []float64{qnan64, snan64, negQnan64})
355 checkPermitsAll(t, ulps(math.MaxFloat64, 1<<30), nil, []float64{snan64})
356
357
358 checkPermitsAll(t, ulps(1.0, 1<<52), []float64{2}, []float64{up(2, 1)})
359
360
361
362 checkPermitsAll(t, ulps(minSub64, 1<<63), []float64{math.MaxFloat64}, []float64{negZero64, -minSub64})
363 checkPermitsAll(t, ulps(1.0, math.MaxUint64), []float64{0, minSub64, math.MaxFloat64}, []float64{negZero64, -1, inf64, qnan64})
364 })
365 }
366
367 func TestPerPlatformDefault(t *testing.T) {
368 v := perPlatformDefault[int32](1, map[Platform]int32{PlatformWASM: 2})
369 for p := range numPlatforms {
370 want := int32(1)
371 if p == PlatformWASM {
372 want = 2
373 }
374 if !v.Permits(want, p) || v.Permits(3-want, p) {
375 t.Errorf("on %v: want only %d to be permitted", p, want)
376 }
377 }
378 if !v.Permits(1, PlatformAny) || !v.Permits(2, PlatformAny) || v.Permits(3, PlatformAny) {
379 t.Errorf("on %v: want only 1 and 2 to be permitted", PlatformAny)
380 }
381 if v.Canon() != 1 {
382 t.Errorf("Canon() = %d, want 1", v.Canon())
383 }
384 }
385
View as plain text