t_fenv.c revision 1.15 1 1.15 riastrad /* $NetBSD: t_fenv.c,v 1.15 2024/02/20 03:53:48 riastradh Exp $ */
2 1.1 martin
3 1.1 martin /*-
4 1.1 martin * Copyright (c) 2014 The NetBSD Foundation, Inc.
5 1.1 martin * All rights reserved.
6 1.1 martin *
7 1.1 martin * This code is derived from software contributed to The NetBSD Foundation
8 1.1 martin * by Martin Husemann.
9 1.1 martin *
10 1.1 martin * Redistribution and use in source and binary forms, with or without
11 1.1 martin * modification, are permitted provided that the following conditions
12 1.1 martin * are met:
13 1.1 martin * 1. Redistributions of source code must retain the above copyright
14 1.1 martin * notice, this list of conditions and the following disclaimer.
15 1.1 martin * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 martin * notice, this list of conditions and the following disclaimer in the
17 1.1 martin * documentation and/or other materials provided with the distribution.
18 1.1 martin *
19 1.1 martin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 martin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 martin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 martin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 martin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 martin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 martin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 martin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 martin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 martin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 martin * POSSIBILITY OF SUCH DAMAGE.
30 1.1 martin */
31 1.1 martin #include <sys/cdefs.h>
32 1.15 riastrad __RCSID("$NetBSD: t_fenv.c,v 1.15 2024/02/20 03:53:48 riastradh Exp $");
33 1.1 martin
34 1.1 martin #include <atf-c.h>
35 1.1 martin
36 1.3 christos #include <fenv.h>
37 1.3 christos #ifdef __HAVE_FENV
38 1.1 martin
39 1.10 riastrad /* XXXGCC gcc lacks #pragma STDC FENV_ACCESS */
40 1.11 riastrad /* XXXclang clang lacks #pragma STDC FENV_ACCESS on some ports */
41 1.10 riastrad #if !defined(__GNUC__)
42 1.10 riastrad #pragma STDC FENV_ACCESS ON
43 1.10 riastrad #endif
44 1.10 riastrad
45 1.8 riastrad #include <float.h>
46 1.1 martin #include <ieeefp.h>
47 1.1 martin #include <stdlib.h>
48 1.1 martin
49 1.10 riastrad #if FLT_RADIX != 2
50 1.10 riastrad #error This test assumes binary floating-point arithmetic.
51 1.10 riastrad #endif
52 1.1 martin
53 1.4 martin #if (__arm__ && !__SOFTFP__) || __aarch64__
54 1.1 martin /*
55 1.6 kamil * Some NEON fpus do not trap on IEEE 754 FP exceptions.
56 1.5 martin * Skip these tests if running on them and compiled for
57 1.1 martin * hard float.
58 1.1 martin */
59 1.2 martin #define FPU_EXC_PREREQ() \
60 1.1 martin if (0 == fpsetmask(fpsetmask(FP_X_INV))) \
61 1.5 martin atf_tc_skip("FPU does not implement traps on FP exceptions");
62 1.2 martin
63 1.2 martin /*
64 1.2 martin * Same as above: some don't allow configuring the rounding mode.
65 1.2 martin */
66 1.2 martin #define FPU_RND_PREREQ() \
67 1.2 martin if (0 == fpsetround(fpsetround(FP_RZ))) \
68 1.2 martin atf_tc_skip("FPU does not implement configurable " \
69 1.2 martin "rounding modes");
70 1.1 martin #endif
71 1.1 martin
72 1.2 martin #ifndef FPU_EXC_PREREQ
73 1.2 martin #define FPU_EXC_PREREQ() /* nothing */
74 1.2 martin #endif
75 1.2 martin #ifndef FPU_RND_PREREQ
76 1.2 martin #define FPU_RND_PREREQ() /* nothing */
77 1.1 martin #endif
78 1.1 martin
79 1.1 martin
80 1.8 riastrad static int
81 1.8 riastrad feround_to_fltrounds(int feround)
82 1.8 riastrad {
83 1.8 riastrad
84 1.8 riastrad /*
85 1.8 riastrad * C99, Sec. 5.2.4.2.2 Characteristics of floating types
86 1.8 riastrad * <float.h>, p. 24, clause 7
87 1.8 riastrad */
88 1.8 riastrad switch (feround) {
89 1.8 riastrad case FE_TOWARDZERO:
90 1.8 riastrad return 0;
91 1.8 riastrad case FE_TONEAREST:
92 1.8 riastrad return 1;
93 1.8 riastrad case FE_UPWARD:
94 1.8 riastrad return 2;
95 1.8 riastrad case FE_DOWNWARD:
96 1.8 riastrad return 3;
97 1.8 riastrad default:
98 1.8 riastrad return -1;
99 1.8 riastrad }
100 1.8 riastrad }
101 1.8 riastrad
102 1.8 riastrad static void
103 1.8 riastrad checkfltrounds(void)
104 1.8 riastrad {
105 1.8 riastrad int feround = fegetround();
106 1.8 riastrad int expected = feround_to_fltrounds(feround);
107 1.8 riastrad
108 1.8 riastrad ATF_CHECK_EQ_MSG(FLT_ROUNDS, expected,
109 1.8 riastrad "FLT_ROUNDS=%d expected=%d fegetround()=%d",
110 1.8 riastrad FLT_ROUNDS, expected, feround);
111 1.8 riastrad }
112 1.8 riastrad
113 1.9 riastrad static void
114 1.11 riastrad checkrounding(int feround, const char *name)
115 1.9 riastrad {
116 1.9 riastrad volatile double ulp1 = DBL_EPSILON;
117 1.12 riastrad
118 1.12 riastrad /*
119 1.12 riastrad * XXX These must be volatile to force rounding to double when
120 1.12 riastrad * the intermediate quantities are evaluated in long double
121 1.12 riastrad * precision, e.g. on 32-bit x86 with x87 long double. Under
122 1.12 riastrad * the C standard (C99, C11, C17, &c.), cast and assignment
123 1.12 riastrad * operators are required to remove all extra range and
124 1.12 riastrad * precision, i.e., round double to long double. But we build
125 1.12 riastrad * this code with -std=gnu99, which diverges from this
126 1.12 riastrad * requirement -- unless you add a volatile qualifier.
127 1.12 riastrad */
128 1.12 riastrad volatile double y1 = -1 + ulp1/4;
129 1.12 riastrad volatile double y2 = 1 + 3*(ulp1/2);
130 1.9 riastrad
131 1.13 riastrad double z1, z2;
132 1.13 riastrad
133 1.9 riastrad switch (feround) {
134 1.13 riastrad case FE_TONEAREST:
135 1.13 riastrad z1 = -1;
136 1.13 riastrad z2 = 1 + 2*ulp1;
137 1.9 riastrad break;
138 1.13 riastrad case FE_TOWARDZERO:
139 1.13 riastrad z1 = -1 + ulp1/2;
140 1.13 riastrad z2 = 1 + ulp1;
141 1.9 riastrad break;
142 1.13 riastrad case FE_UPWARD:
143 1.13 riastrad z1 = -1 + ulp1/2;
144 1.13 riastrad z2 = 1 + 2*ulp1;
145 1.9 riastrad break;
146 1.13 riastrad case FE_DOWNWARD:
147 1.13 riastrad z1 = -1;
148 1.13 riastrad z2 = 1 + ulp1;
149 1.9 riastrad break;
150 1.13 riastrad default:
151 1.13 riastrad atf_tc_fail("unknown rounding mode %d (%s)", feround, name);
152 1.9 riastrad }
153 1.13 riastrad
154 1.13 riastrad ATF_CHECK_EQ_MSG(y1, z1, "%s[-1 + ulp(1)/4] expected=%a actual=%a",
155 1.13 riastrad name, y1, z1);
156 1.13 riastrad ATF_CHECK_EQ_MSG(y2, z2, "%s[1 + 3*(ulp(1)/2)] expected=%a actual=%a",
157 1.13 riastrad name, y2, z2);
158 1.9 riastrad }
159 1.9 riastrad
160 1.1 martin ATF_TC(fegetround);
161 1.1 martin
162 1.1 martin ATF_TC_HEAD(fegetround, tc)
163 1.1 martin {
164 1.1 martin atf_tc_set_md_var(tc, "descr",
165 1.1 martin "verify the fegetround() function agrees with the legacy "
166 1.1 martin "fpsetround");
167 1.1 martin }
168 1.1 martin
169 1.1 martin ATF_TC_BODY(fegetround, tc)
170 1.1 martin {
171 1.2 martin FPU_RND_PREREQ();
172 1.2 martin
173 1.11 riastrad checkrounding(FE_TONEAREST, "FE_TONEAREST");
174 1.9 riastrad
175 1.1 martin fpsetround(FP_RZ);
176 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_TOWARDZERO,
177 1.7 riastrad "fegetround()=%d FE_TOWARDZERO=%d",
178 1.7 riastrad fegetround(), FE_TOWARDZERO);
179 1.8 riastrad checkfltrounds();
180 1.11 riastrad checkrounding(FE_TOWARDZERO, "FE_TOWARDZERO");
181 1.8 riastrad
182 1.1 martin fpsetround(FP_RM);
183 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_DOWNWARD,
184 1.7 riastrad "fegetround()=%d FE_DOWNWARD=%d",
185 1.7 riastrad fegetround(), FE_DOWNWARD);
186 1.8 riastrad checkfltrounds();
187 1.11 riastrad checkrounding(FE_DOWNWARD, "FE_DOWNWARD");
188 1.8 riastrad
189 1.1 martin fpsetround(FP_RN);
190 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_TONEAREST,
191 1.7 riastrad "fegetround()=%d FE_TONEAREST=%d",
192 1.7 riastrad fegetround(), FE_TONEAREST);
193 1.8 riastrad checkfltrounds();
194 1.11 riastrad checkrounding(FE_TONEAREST, "FE_TONEAREST");
195 1.8 riastrad
196 1.1 martin fpsetround(FP_RP);
197 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_UPWARD,
198 1.7 riastrad "fegetround()=%d FE_UPWARD=%d",
199 1.7 riastrad fegetround(), FE_UPWARD);
200 1.8 riastrad checkfltrounds();
201 1.11 riastrad checkrounding(FE_UPWARD, "FE_UPWARD");
202 1.1 martin }
203 1.1 martin
204 1.1 martin ATF_TC(fesetround);
205 1.1 martin
206 1.1 martin ATF_TC_HEAD(fesetround, tc)
207 1.1 martin {
208 1.1 martin atf_tc_set_md_var(tc, "descr",
209 1.1 martin "verify the fesetround() function agrees with the legacy "
210 1.1 martin "fpgetround");
211 1.1 martin }
212 1.1 martin
213 1.1 martin ATF_TC_BODY(fesetround, tc)
214 1.1 martin {
215 1.2 martin FPU_RND_PREREQ();
216 1.2 martin
217 1.11 riastrad checkrounding(FE_TONEAREST, "FE_TONEAREST");
218 1.9 riastrad
219 1.1 martin fesetround(FE_TOWARDZERO);
220 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RZ,
221 1.7 riastrad "fpgetround()=%d FP_RZ=%d",
222 1.7 riastrad (int)fpgetround(), (int)FP_RZ);
223 1.8 riastrad checkfltrounds();
224 1.11 riastrad checkrounding(FE_TOWARDZERO, "FE_TOWARDZERO");
225 1.8 riastrad
226 1.1 martin fesetround(FE_DOWNWARD);
227 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RM,
228 1.7 riastrad "fpgetround()=%d FP_RM=%d",
229 1.7 riastrad (int)fpgetround(), (int)FP_RM);
230 1.8 riastrad checkfltrounds();
231 1.11 riastrad checkrounding(FE_DOWNWARD, "FE_DOWNWARD");
232 1.8 riastrad
233 1.1 martin fesetround(FE_TONEAREST);
234 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RN,
235 1.7 riastrad "fpgetround()=%d FP_RN=%d",
236 1.7 riastrad (int)fpgetround(), (int)FP_RN);
237 1.8 riastrad checkfltrounds();
238 1.11 riastrad checkrounding(FE_TONEAREST, "FE_TONEAREST");
239 1.8 riastrad
240 1.1 martin fesetround(FE_UPWARD);
241 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RP,
242 1.7 riastrad "fpgetround()=%d FP_RP=%d",
243 1.7 riastrad (int)fpgetround(), (int)FP_RP);
244 1.8 riastrad checkfltrounds();
245 1.11 riastrad checkrounding(FE_UPWARD, "FE_UPWARD");
246 1.1 martin }
247 1.1 martin
248 1.1 martin ATF_TC(fegetexcept);
249 1.1 martin
250 1.1 martin ATF_TC_HEAD(fegetexcept, tc)
251 1.1 martin {
252 1.1 martin atf_tc_set_md_var(tc, "descr",
253 1.1 martin "verify the fegetexcept() function agrees with the legacy "
254 1.1 martin "fpsetmask()");
255 1.1 martin }
256 1.1 martin
257 1.1 martin ATF_TC_BODY(fegetexcept, tc)
258 1.1 martin {
259 1.2 martin FPU_EXC_PREREQ();
260 1.1 martin
261 1.1 martin fpsetmask(0);
262 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), 0,
263 1.7 riastrad "fegetexcept()=%d",
264 1.7 riastrad fegetexcept());
265 1.1 martin
266 1.1 martin fpsetmask(FP_X_INV|FP_X_DZ|FP_X_OFL|FP_X_UFL|FP_X_IMP);
267 1.1 martin ATF_CHECK(fegetexcept() == (FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW
268 1.1 martin |FE_UNDERFLOW|FE_INEXACT));
269 1.1 martin
270 1.1 martin fpsetmask(FP_X_INV);
271 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_INVALID,
272 1.7 riastrad "fegetexcept()=%d FE_INVALID=%d",
273 1.7 riastrad fegetexcept(), FE_INVALID);
274 1.1 martin
275 1.1 martin fpsetmask(FP_X_DZ);
276 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_DIVBYZERO,
277 1.7 riastrad "fegetexcept()=%d FE_DIVBYZERO=%d",
278 1.7 riastrad fegetexcept(), FE_DIVBYZERO);
279 1.1 martin
280 1.1 martin fpsetmask(FP_X_OFL);
281 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_OVERFLOW,
282 1.7 riastrad "fegetexcept()=%d FE_OVERFLOW=%d",
283 1.7 riastrad fegetexcept(), FE_OVERFLOW);
284 1.1 martin
285 1.1 martin fpsetmask(FP_X_UFL);
286 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_UNDERFLOW,
287 1.7 riastrad "fegetexcept()=%d FE_UNDERFLOW=%d",
288 1.7 riastrad fegetexcept(), FE_UNDERFLOW);
289 1.1 martin
290 1.1 martin fpsetmask(FP_X_IMP);
291 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_INEXACT,
292 1.7 riastrad "fegetexcept()=%d FE_INEXACT=%d",
293 1.7 riastrad fegetexcept(), FE_INEXACT);
294 1.1 martin }
295 1.1 martin
296 1.1 martin ATF_TC(feenableexcept);
297 1.1 martin
298 1.1 martin ATF_TC_HEAD(feenableexcept, tc)
299 1.1 martin {
300 1.1 martin atf_tc_set_md_var(tc, "descr",
301 1.1 martin "verify the feenableexcept() function agrees with the legacy "
302 1.1 martin "fpgetmask()");
303 1.1 martin }
304 1.1 martin
305 1.1 martin ATF_TC_BODY(feenableexcept, tc)
306 1.1 martin {
307 1.2 martin FPU_EXC_PREREQ();
308 1.1 martin
309 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
310 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), 0,
311 1.7 riastrad "fpgetmask()=%d",
312 1.7 riastrad (int)fpgetmask());
313 1.1 martin
314 1.1 martin feenableexcept(FE_UNDERFLOW);
315 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_UFL,
316 1.7 riastrad "fpgetmask()=%d FP_X_UFL=%d",
317 1.7 riastrad (int)fpgetmask(), (int)FP_X_UFL);
318 1.1 martin
319 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
320 1.1 martin feenableexcept(FE_OVERFLOW);
321 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_OFL,
322 1.7 riastrad "fpgetmask()=%d FP_X_OFL=%d",
323 1.7 riastrad (int)fpgetmask(), (int)FP_X_OFL);
324 1.1 martin
325 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
326 1.1 martin feenableexcept(FE_DIVBYZERO);
327 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_DZ,
328 1.7 riastrad "fpgetmask()=%d FP_X_DZ=%d",
329 1.7 riastrad (int)fpgetmask(), (int)FP_X_DZ);
330 1.1 martin
331 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
332 1.1 martin feenableexcept(FE_INEXACT);
333 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_IMP,
334 1.7 riastrad "fpgetmask()=%d FP_X_IMP=%d",
335 1.7 riastrad (int)fpgetmask(), (int)FP_X_IMP);
336 1.1 martin
337 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
338 1.1 martin feenableexcept(FE_INVALID);
339 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_INV,
340 1.7 riastrad "fpgetmask()=%d FP_X_INV=%d",
341 1.7 riastrad (int)fpgetmask(), (int)FP_X_INV);
342 1.1 martin }
343 1.1 martin
344 1.14 riastrad ATF_TC(fetestexcept_trap);
345 1.14 riastrad ATF_TC_HEAD(fetestexcept_trap, tc)
346 1.14 riastrad {
347 1.14 riastrad atf_tc_set_md_var(tc, "descr",
348 1.14 riastrad "Verify fetestexcept doesn't affect the trapped excpetions");
349 1.14 riastrad }
350 1.14 riastrad ATF_TC_BODY(fetestexcept_trap, tc)
351 1.14 riastrad {
352 1.14 riastrad int except;
353 1.14 riastrad
354 1.14 riastrad fedisableexcept(FE_ALL_EXCEPT);
355 1.14 riastrad ATF_CHECK_EQ_MSG((except = fegetexcept()), 0,
356 1.14 riastrad "fegetexcept()=0x%x", except);
357 1.14 riastrad
358 1.14 riastrad (void)fetestexcept(FE_ALL_EXCEPT);
359 1.14 riastrad ATF_CHECK_EQ_MSG((except = fegetexcept()), 0,
360 1.14 riastrad "fegetexcept()=0x%x", except);
361 1.14 riastrad
362 1.14 riastrad feenableexcept(FE_ALL_EXCEPT);
363 1.14 riastrad ATF_CHECK_EQ_MSG((except = fegetexcept()), FE_ALL_EXCEPT,
364 1.14 riastrad "fegetexcept()=0x%x FE_ALL_EXCEPT=0x%x", except, FE_ALL_EXCEPT);
365 1.14 riastrad
366 1.14 riastrad (void)fetestexcept(FE_ALL_EXCEPT);
367 1.14 riastrad ATF_CHECK_EQ_MSG((except = fegetexcept()), FE_ALL_EXCEPT,
368 1.14 riastrad "fegetexcept()=0x%x FE_ALL_EXCEPT=0x%x", except, FE_ALL_EXCEPT);
369 1.14 riastrad }
370 1.14 riastrad
371 1.1 martin ATF_TP_ADD_TCS(tp)
372 1.1 martin {
373 1.1 martin ATF_TP_ADD_TC(tp, fegetround);
374 1.1 martin ATF_TP_ADD_TC(tp, fesetround);
375 1.1 martin ATF_TP_ADD_TC(tp, fegetexcept);
376 1.1 martin ATF_TP_ADD_TC(tp, feenableexcept);
377 1.14 riastrad ATF_TP_ADD_TC(tp, fetestexcept_trap);
378 1.1 martin
379 1.1 martin return atf_no_error();
380 1.1 martin }
381 1.1 martin
382 1.1 martin #else /* no fenv.h support */
383 1.1 martin
384 1.1 martin ATF_TC(t_nofenv);
385 1.1 martin
386 1.1 martin ATF_TC_HEAD(t_nofenv, tc)
387 1.1 martin {
388 1.1 martin atf_tc_set_md_var(tc, "descr",
389 1.1 martin "dummy test case - no fenv.h support");
390 1.1 martin }
391 1.1 martin
392 1.1 martin
393 1.1 martin ATF_TC_BODY(t_nofenv, tc)
394 1.1 martin {
395 1.1 martin atf_tc_skip("no fenv.h support on this architecture");
396 1.1 martin }
397 1.1 martin
398 1.1 martin ATF_TP_ADD_TCS(tp)
399 1.1 martin {
400 1.1 martin ATF_TP_ADD_TC(tp, t_nofenv);
401 1.1 martin return atf_no_error();
402 1.1 martin }
403 1.1 martin
404 1.1 martin #endif
405