1 /**
2  * Copyright (c) 2024 Raspberry Pi Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdio.h>
8 #include "pico/stdlib.h"
9 
10 // This test covers the single-precision functions in:
11 //
12 //    src/pico_float/float_hazard3_single.S
13 //
14 // It assumes the canonical generated-NaN value and NaN sign rules used by
15 // those functions (which are unspecified by IEEE 754). It does not cover
16 // libgcc/libm functions from outside of that source file.
17 
18 typedef struct {
19     uint32_t x;
20     uint32_t y;
21     uint32_t expect;
22 } test_t;
23 
24 test_t add_directed_tests[] = {
25     // 1 + 1 = 2
26     {0x3f800000u, 0x3f800000u, 0x40000000u},
27     // 2 + 1 = 3
28     {0x40000000u, 0x3f800000u, 0x40400000u},
29     // 1 + 2 = 3
30     {0x3f800000u, 0x40000000u, 0x40400000u},
31     // 1 + -1 = +0 (exact cancellation)
32     {0x3f800000u, 0xbf800000u, 0x00000000u},
33     // -1 + 1 = +0 (exact cancellation)
34     {0xbf800000u, 0x3f800000u, 0x00000000u},
35     // 1 + <<1 ulp = 1
36     {0x3f800000u, 0x2f800000u, 0x3f800000u},
37     // <<1 ulp + 1 = 1
38     {0x2f800000u, 0x3f800000u, 0x3f800000u},
39     // -1 + 1.25 = 0.25
40     {0xbf800000u, 0x3fa00000u, 0x3e800000u},
41     // max normal + 0.5 ulp = +inf
42     {0x7f7fffffu, 0x73000000u, 0x7f800000u},
43     // max normal + max normal = +inf
44     {0x7f7fffffu, 0x7f7fffffu, 0x7f800000u},
45     // min normal - 0.5 ulp = -inf
46     {0xff7fffffu, 0xf3000000u, 0xff800000u},
47     // min normal + min_normal = -inf
48     {0xff7fffffu, 0xff7fffffu, 0xff800000u},
49     // max normal + 0.499... ulp = max normal
50     {0x7f7fffffu, 0x72ffffffu, 0x7f7fffffu},
51     // min normal - 0.499... ulp = min normal
52     {0xff7fffffu, 0xf2ffffffu, 0xff7fffffu},
53     // nan + 0 = same nan
54     {0xffff1234u, 0x00000000u, 0xffff1234u},
55     // 0 + nan = same nan
56     {0x00000000u, 0xffff1234u, 0xffff1234u},
57     // nan + 1 = same nan
58     {0xffff1234u, 0x3f800000u, 0xffff1234u},
59     // 1 + nan = same nan
60     {0x3f800000u, 0xffff1234u, 0xffff1234u},
61     // nan + inf = same nan
62     {0xffff1234u, 0x7f800000u, 0xffff1234u},
63     // inf + nan = same nan
64     {0x7f800000u, 0xffff1234u, 0xffff1234u},
65     // inf + inf = inf
66     {0x7f800000u, 0x7f800000u, 0x7f800000u},
67     // -inf + -inf = -inf
68     {0xff800000u, 0xff800000u, 0xff800000u},
69     // inf + -inf = nan (all-ones is our canonical cheap nan)
70     {0x7f800000u, 0xff800000u, 0xffffffffu},
71     // -inf + inf = nan
72     {0xff800000u, 0x7f800000u, 0xffffffffu},
73     // subnormal + subnormal = exactly 0
74     {0x007fffffu, 0x007fffffu, 0x00000000u},
75     // -subnormal + -subnormal = exactly -0
76     {0x807fffffu, 0x807fffffu, 0x80000000u},
77     // Even + 0.5 ulp: round down
78     {0x3f800002u, 0x33800000u, 0x3f800002u},
79     // Even - 0.5 ulp: round up
80     {0x3f800002u, 0xb3800000u, 0x3f800002u},
81     // Odd + 0.5 ulp: round up
82     {0x3f800001u, 0x33800000u, 0x3f800002u},
83     // Odd - 0.5 ulp: round down
84     {0x3f800001u, 0xb3800000u, 0x3f800000u},
85     // All-zeroes significand - 0.5 ulp: no rounding (exact)
86     {0x3f800000u, 0xb3800000u, 0x3f7fffffu},
87     // Very subnormal difference of normals: flushed to zero
88     {0x03800000u, 0x837fffffu, 0x00000000u},
89     // Barely subnormal difference of normals: also flushed (unflushed result is 2^(emin-1))
90     {0x03800000u, 0x837e0000u, 0x00000000u},
91 };
92 
93 test_t mul_directed_tests[] = {
94     // -- directed tests --
95     // 1 * 1 = 1
96     {0x3f800000u, 0x3f800000u, 0x3f800000u},
97     // 1 * -1 = -1
98     {0x3f800000u, 0xbf800000u, 0xbf800000u},
99     // -1 * 1 = -1
100     {0xbf800000u, 0x3f800000u, 0xbf800000u},
101     // -1 * -1 = 1
102     {0xbf800000u, 0xbf800000u, 0x3f800000u},
103     // -0 * 0 = -0
104     {0x80000000u, 0x00000000u, 0x80000000u},
105     // 0 * -0 = - 0
106     {0x00000000u, 0x80000000u, 0x80000000u},
107     // 1 * 2 = 2
108     {0x3f800000u, 0x40000000u, 0x40000000u},
109     // 2 * 1 = 2
110     {0x40000000u, 0x3f800000u, 0x40000000u},
111     // inf * inf = inf
112     {0x7f800000u, 0x7f800000u, 0x7f800000u},
113     // inf * -inf = -inf
114     {0x7f800000u, 0xff800000u, 0xff800000u},
115     // inf * 0 = nan
116     {0x7f800000u, 0x00000000u, 0xffffffffu},
117     // 0 * inf = nan
118     {0x00000000u, 0x7f800000u, 0xffffffffu},
119     // 1 * -inf = -inf
120     {0x3f800000u, 0xff800000u, 0xff800000u},
121     // -inf * 1 = -inf
122     {0xff800000u, 0x3f800000u, 0xff800000u},
123     // -1 * inf = -inf
124     {0xbf800000u, 0x7f800000u, 0xff800000u},
125     // inf * -1 = -inf
126     {0x7f800000u, 0xbf800000u, 0xff800000u},
127     // 1 * nonzero subnormal = exactly 0
128     {0x3f800000u, 0x007fffffu, 0x00000000u},
129     // nonzero subnormal * -1 = exactly -0
130     {0x007fffffu, 0xbf800000u, 0x80000000u},
131     // nan * 0 = same nan
132     {0xffff1234u, 0x00000000u, 0xffff1234u},
133     // 0 * nan = same nan
134     {0x00000000u, 0xffff1234u, 0xffff1234u},
135     // nan * 1 = same nan
136     {0xffff1234u, 0x3f800000u, 0xffff1234u},
137     // 1 * nan = same nan
138     {0x3f800000u, 0xffff1234u, 0xffff1234u},
139     // nan * inf = same nan
140     {0xffff1234u, 0x7f800000u, 0xffff1234u},
141     // inf * nan = same nan
142     {0x7f800000u, 0xffff1234u, 0xffff1234u},
143     // (2 - 0.5 ulp) x (2 - 0.5 ulp) = 4 - 0.5 ulp
144     {0x3fffffffu, 0x3fffffffu, 0x407ffffeu},
145     // (2 - 0.5 ulp) x (1 + 1 ulp) = 2 exactly
146     {0xbfffffffu, 0x3f800001u, 0xc0000000u},
147     // 1.666... * 1.333.. = 2.222...
148     {0x3fd55555u, 0x3faaaaaau, 0x400e38e3u},
149     // 1.25 x 2^-63 x 1.25 x 2^-64 = 0
150     // (normal inputs with subnormal output, and we claim to be FTZ)
151     {0x20200000u, 0x1fa00000u, 0x00000000u},
152     // 1.333333 (rounded down) x 1.5 = 2 - 1 ulp
153     {0x3faaaaaau, 0x3fc00000u, 0x3fffffffu},
154     // 1.333333 (rounded down) x (1.5 + 1 ulp) = 2 exactly
155     {0x3faaaaaau, 0x3fc00001u, 0x40000000u},
156     // (1.333333 (rounded down) + 1 ulp) x 1.5 = 2 exactly
157     {0x3faaaaabu, 0x3fc00000u, 0x40000000u},
158     // (1.25 - 1 ulp) x (0.8 + 1 ulp) = 1 exactly (exponent increases after rounding)
159     {0x3f9fffffu, 0x3f4cccceu, 0x3f800000u},
160     // as above, but overflow on exponent increase -> +inf
161     {0x3f9fffffu, 0x7f4cccceu, 0x7f800000u},
162     // subtract 1 ulp from rhs -> largest normal
163     {0x3f9fffffu, 0x7f4ccccdu, 0x7f7fffffu},
164 };
165 
166 #define N_RANDOM_TESTS 1000
167 extern test_t add_random_tests[N_RANDOM_TESTS];
168 extern test_t mul_random_tests[N_RANDOM_TESTS];
169 
170 uint32_t __addsf3(uint32_t x, uint32_t y);
171 uint32_t __mulsf3(uint32_t x, uint32_t y);
172 
run_tests(test_t * tests,int n_tests,const char * op_str,uint32_t (* func)(uint32_t,uint32_t))173 int run_tests(test_t *tests, int n_tests, const char *op_str, uint32_t (*func)(uint32_t, uint32_t)) {
174     int failed = 0;
175     for (int i = 0; i < n_tests; ++i) {
176         uint32_t actual = func(tests[i].x, tests[i].y);
177         if (tests[i].expect != actual) {
178             printf("%08x %s %08x -> %08x", tests[i].x, op_str, tests[i].y, tests[i].expect);
179             printf("  FAIL: got %08x\n", actual);
180             ++failed;
181         }
182     }
183     printf("Passed: %d / %d\n", n_tests - failed, n_tests);
184     return failed;
185 }
186 
main()187 int main() {
188     stdio_init_all();
189     int failed = 0;
190     sleep_ms(3000);
191     printf("Testing: __addsf3 (directed tests)\n");
192     failed += run_tests(add_directed_tests, count_of(add_directed_tests), "+", __addsf3);
193     printf("Testing: __mulsf3 (directed tests)\n");
194     failed += run_tests(mul_directed_tests, count_of(mul_directed_tests), "*", __mulsf3);
195     if (failed) {
196         printf("Skipping random tests due to %d test failures\n", failed);
197         goto done;
198     }
199     printf("Testing: __addsf3 (random tests)\n");
200     failed += run_tests(add_random_tests, N_RANDOM_TESTS, "+", __addsf3);
201     printf("Testing: __mulsf3 (random tests)\n");
202     failed += run_tests(mul_random_tests, N_RANDOM_TESTS, "*", __mulsf3);
203 
204     printf("%d tests failed.\n", failed);
205     if (failed == 0) {
206         printf("Well done, you can relax now\n");
207     }
208 done:
209     while (true) {asm volatile ("wfi\n");} // keep USB stdout alive
210     return 0;
211 }
212 
213 // Generated using the FPU on my machine (Zen 4) plus FTZ on inputs/outputs
214 // See hazard3_test_gen.c
215 test_t add_random_tests[N_RANDOM_TESTS] = {
216 #include "vectors/hazard3_addsf.inc"
217 };
218 
219 test_t mul_random_tests[N_RANDOM_TESTS] = {
220 #include "vectors/hazard3_mulsf.inc"
221 };
222