t_fenv.c revision 1.11 1 1.11 riastrad /* $NetBSD: t_fenv.c,v 1.11 2023/11/05 21:13:06 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.11 riastrad __RCSID("$NetBSD: t_fenv.c,v 1.11 2023/11/05 21:13:06 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.9 riastrad double y1 = -1 + ulp1/4;
118 1.9 riastrad double y2 = 1 + 3*(ulp1/2);
119 1.9 riastrad
120 1.9 riastrad switch (feround) {
121 1.9 riastrad case FE_TONEAREST: {
122 1.9 riastrad double z1 = -1;
123 1.9 riastrad double z2 = 1 + 2*ulp1;
124 1.11 riastrad ATF_CHECK_EQ_MSG(y1, z1, "%s[-1 + ulp(1)/4]"
125 1.11 riastrad " expected=%a actual=%a",
126 1.11 riastrad name, y1, z1);
127 1.11 riastrad ATF_CHECK_EQ_MSG(y2, z2, "%s[1 + 3*(ulp(1)/2)]"
128 1.11 riastrad " expected=%a actual=%a",
129 1.11 riastrad name, y2, z2);
130 1.9 riastrad break;
131 1.9 riastrad }
132 1.9 riastrad case FE_TOWARDZERO: {
133 1.9 riastrad double z1 = -1 + ulp1/2;
134 1.9 riastrad double z2 = 1 + ulp1;
135 1.11 riastrad ATF_CHECK_EQ_MSG(y1, z1, "%s[-1 + ulp(1)/4]"
136 1.11 riastrad " expected=%a actual=%a",
137 1.11 riastrad name, y1, z1);
138 1.11 riastrad ATF_CHECK_EQ_MSG(y2, z2, "%s[1 + 3*(ulp(1)/2)]"
139 1.11 riastrad " expected=%a actual=%a",
140 1.11 riastrad name, y2, z2);
141 1.9 riastrad break;
142 1.9 riastrad }
143 1.9 riastrad case FE_UPWARD: {
144 1.9 riastrad double z1 = -1 + ulp1/2;
145 1.9 riastrad double z2 = 1 + 2*ulp1;
146 1.11 riastrad ATF_CHECK_EQ_MSG(y1, z1, "%s[-1 + ulp(1)/4]"
147 1.11 riastrad " expected=%a actual=%a",
148 1.11 riastrad name, y1, z1);
149 1.11 riastrad ATF_CHECK_EQ_MSG(y2, z2, "%s[1 + 3*(ulp(1)/2)]"
150 1.11 riastrad " expected=%a actual=%a",
151 1.11 riastrad name, y2, z2);
152 1.9 riastrad break;
153 1.9 riastrad }
154 1.9 riastrad case FE_DOWNWARD: {
155 1.9 riastrad double z1 = -1;
156 1.9 riastrad double z2 = 1 + ulp1;
157 1.11 riastrad ATF_CHECK_EQ_MSG(y1, z1, "%s[-1 + ulp(1)/4]"
158 1.11 riastrad " expected=%a actual=%a",
159 1.11 riastrad name, y1, z1);
160 1.11 riastrad ATF_CHECK_EQ_MSG(y2, z2, "%s[1 + 3*(ulp(1)/2)]"
161 1.11 riastrad " expected=%a actual=%a",
162 1.11 riastrad name, y2, z2);
163 1.9 riastrad break;
164 1.9 riastrad }
165 1.9 riastrad }
166 1.9 riastrad }
167 1.9 riastrad
168 1.1 martin ATF_TC(fegetround);
169 1.1 martin
170 1.1 martin ATF_TC_HEAD(fegetround, tc)
171 1.1 martin {
172 1.1 martin atf_tc_set_md_var(tc, "descr",
173 1.1 martin "verify the fegetround() function agrees with the legacy "
174 1.1 martin "fpsetround");
175 1.1 martin }
176 1.1 martin
177 1.1 martin ATF_TC_BODY(fegetround, tc)
178 1.1 martin {
179 1.2 martin FPU_RND_PREREQ();
180 1.2 martin
181 1.11 riastrad checkrounding(FE_TONEAREST, "FE_TONEAREST");
182 1.9 riastrad
183 1.1 martin fpsetround(FP_RZ);
184 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_TOWARDZERO,
185 1.7 riastrad "fegetround()=%d FE_TOWARDZERO=%d",
186 1.7 riastrad fegetround(), FE_TOWARDZERO);
187 1.8 riastrad checkfltrounds();
188 1.11 riastrad checkrounding(FE_TOWARDZERO, "FE_TOWARDZERO");
189 1.8 riastrad
190 1.1 martin fpsetround(FP_RM);
191 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_DOWNWARD,
192 1.7 riastrad "fegetround()=%d FE_DOWNWARD=%d",
193 1.7 riastrad fegetround(), FE_DOWNWARD);
194 1.8 riastrad checkfltrounds();
195 1.11 riastrad checkrounding(FE_DOWNWARD, "FE_DOWNWARD");
196 1.8 riastrad
197 1.1 martin fpsetround(FP_RN);
198 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_TONEAREST,
199 1.7 riastrad "fegetround()=%d FE_TONEAREST=%d",
200 1.7 riastrad fegetround(), FE_TONEAREST);
201 1.8 riastrad checkfltrounds();
202 1.11 riastrad checkrounding(FE_TONEAREST, "FE_TONEAREST");
203 1.8 riastrad
204 1.1 martin fpsetround(FP_RP);
205 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_UPWARD,
206 1.7 riastrad "fegetround()=%d FE_UPWARD=%d",
207 1.7 riastrad fegetround(), FE_UPWARD);
208 1.8 riastrad checkfltrounds();
209 1.11 riastrad checkrounding(FE_UPWARD, "FE_UPWARD");
210 1.1 martin }
211 1.1 martin
212 1.1 martin ATF_TC(fesetround);
213 1.1 martin
214 1.1 martin ATF_TC_HEAD(fesetround, tc)
215 1.1 martin {
216 1.1 martin atf_tc_set_md_var(tc, "descr",
217 1.1 martin "verify the fesetround() function agrees with the legacy "
218 1.1 martin "fpgetround");
219 1.1 martin }
220 1.1 martin
221 1.1 martin ATF_TC_BODY(fesetround, tc)
222 1.1 martin {
223 1.2 martin FPU_RND_PREREQ();
224 1.2 martin
225 1.11 riastrad checkrounding(FE_TONEAREST, "FE_TONEAREST");
226 1.9 riastrad
227 1.1 martin fesetround(FE_TOWARDZERO);
228 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RZ,
229 1.7 riastrad "fpgetround()=%d FP_RZ=%d",
230 1.7 riastrad (int)fpgetround(), (int)FP_RZ);
231 1.8 riastrad checkfltrounds();
232 1.11 riastrad checkrounding(FE_TOWARDZERO, "FE_TOWARDZERO");
233 1.8 riastrad
234 1.1 martin fesetround(FE_DOWNWARD);
235 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RM,
236 1.7 riastrad "fpgetround()=%d FP_RM=%d",
237 1.7 riastrad (int)fpgetround(), (int)FP_RM);
238 1.8 riastrad checkfltrounds();
239 1.11 riastrad checkrounding(FE_DOWNWARD, "FE_DOWNWARD");
240 1.8 riastrad
241 1.1 martin fesetround(FE_TONEAREST);
242 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RN,
243 1.7 riastrad "fpgetround()=%d FP_RN=%d",
244 1.7 riastrad (int)fpgetround(), (int)FP_RN);
245 1.8 riastrad checkfltrounds();
246 1.11 riastrad checkrounding(FE_TONEAREST, "FE_TONEAREST");
247 1.8 riastrad
248 1.1 martin fesetround(FE_UPWARD);
249 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RP,
250 1.7 riastrad "fpgetround()=%d FP_RP=%d",
251 1.7 riastrad (int)fpgetround(), (int)FP_RP);
252 1.8 riastrad checkfltrounds();
253 1.11 riastrad checkrounding(FE_UPWARD, "FE_UPWARD");
254 1.1 martin }
255 1.1 martin
256 1.1 martin ATF_TC(fegetexcept);
257 1.1 martin
258 1.1 martin ATF_TC_HEAD(fegetexcept, tc)
259 1.1 martin {
260 1.1 martin atf_tc_set_md_var(tc, "descr",
261 1.1 martin "verify the fegetexcept() function agrees with the legacy "
262 1.1 martin "fpsetmask()");
263 1.1 martin }
264 1.1 martin
265 1.1 martin ATF_TC_BODY(fegetexcept, tc)
266 1.1 martin {
267 1.2 martin FPU_EXC_PREREQ();
268 1.1 martin
269 1.1 martin fpsetmask(0);
270 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), 0,
271 1.7 riastrad "fegetexcept()=%d",
272 1.7 riastrad fegetexcept());
273 1.1 martin
274 1.1 martin fpsetmask(FP_X_INV|FP_X_DZ|FP_X_OFL|FP_X_UFL|FP_X_IMP);
275 1.1 martin ATF_CHECK(fegetexcept() == (FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW
276 1.1 martin |FE_UNDERFLOW|FE_INEXACT));
277 1.1 martin
278 1.1 martin fpsetmask(FP_X_INV);
279 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_INVALID,
280 1.7 riastrad "fegetexcept()=%d FE_INVALID=%d",
281 1.7 riastrad fegetexcept(), FE_INVALID);
282 1.1 martin
283 1.1 martin fpsetmask(FP_X_DZ);
284 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_DIVBYZERO,
285 1.7 riastrad "fegetexcept()=%d FE_DIVBYZERO=%d",
286 1.7 riastrad fegetexcept(), FE_DIVBYZERO);
287 1.1 martin
288 1.1 martin fpsetmask(FP_X_OFL);
289 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_OVERFLOW,
290 1.7 riastrad "fegetexcept()=%d FE_OVERFLOW=%d",
291 1.7 riastrad fegetexcept(), FE_OVERFLOW);
292 1.1 martin
293 1.1 martin fpsetmask(FP_X_UFL);
294 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_UNDERFLOW,
295 1.7 riastrad "fegetexcept()=%d FE_UNDERFLOW=%d",
296 1.7 riastrad fegetexcept(), FE_UNDERFLOW);
297 1.1 martin
298 1.1 martin fpsetmask(FP_X_IMP);
299 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_INEXACT,
300 1.7 riastrad "fegetexcept()=%d FE_INEXACT=%d",
301 1.7 riastrad fegetexcept(), FE_INEXACT);
302 1.1 martin }
303 1.1 martin
304 1.1 martin ATF_TC(feenableexcept);
305 1.1 martin
306 1.1 martin ATF_TC_HEAD(feenableexcept, tc)
307 1.1 martin {
308 1.1 martin atf_tc_set_md_var(tc, "descr",
309 1.1 martin "verify the feenableexcept() function agrees with the legacy "
310 1.1 martin "fpgetmask()");
311 1.1 martin }
312 1.1 martin
313 1.1 martin ATF_TC_BODY(feenableexcept, tc)
314 1.1 martin {
315 1.2 martin FPU_EXC_PREREQ();
316 1.1 martin
317 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
318 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), 0,
319 1.7 riastrad "fpgetmask()=%d",
320 1.7 riastrad (int)fpgetmask());
321 1.1 martin
322 1.1 martin feenableexcept(FE_UNDERFLOW);
323 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_UFL,
324 1.7 riastrad "fpgetmask()=%d FP_X_UFL=%d",
325 1.7 riastrad (int)fpgetmask(), (int)FP_X_UFL);
326 1.1 martin
327 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
328 1.1 martin feenableexcept(FE_OVERFLOW);
329 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_OFL,
330 1.7 riastrad "fpgetmask()=%d FP_X_OFL=%d",
331 1.7 riastrad (int)fpgetmask(), (int)FP_X_OFL);
332 1.1 martin
333 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
334 1.1 martin feenableexcept(FE_DIVBYZERO);
335 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_DZ,
336 1.7 riastrad "fpgetmask()=%d FP_X_DZ=%d",
337 1.7 riastrad (int)fpgetmask(), (int)FP_X_DZ);
338 1.1 martin
339 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
340 1.1 martin feenableexcept(FE_INEXACT);
341 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_IMP,
342 1.7 riastrad "fpgetmask()=%d FP_X_IMP=%d",
343 1.7 riastrad (int)fpgetmask(), (int)FP_X_IMP);
344 1.1 martin
345 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
346 1.1 martin feenableexcept(FE_INVALID);
347 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_INV,
348 1.7 riastrad "fpgetmask()=%d FP_X_INV=%d",
349 1.7 riastrad (int)fpgetmask(), (int)FP_X_INV);
350 1.1 martin }
351 1.1 martin
352 1.1 martin ATF_TP_ADD_TCS(tp)
353 1.1 martin {
354 1.1 martin ATF_TP_ADD_TC(tp, fegetround);
355 1.1 martin ATF_TP_ADD_TC(tp, fesetround);
356 1.1 martin ATF_TP_ADD_TC(tp, fegetexcept);
357 1.1 martin ATF_TP_ADD_TC(tp, feenableexcept);
358 1.1 martin
359 1.1 martin return atf_no_error();
360 1.1 martin }
361 1.1 martin
362 1.1 martin #else /* no fenv.h support */
363 1.1 martin
364 1.1 martin ATF_TC(t_nofenv);
365 1.1 martin
366 1.1 martin ATF_TC_HEAD(t_nofenv, tc)
367 1.1 martin {
368 1.1 martin atf_tc_set_md_var(tc, "descr",
369 1.1 martin "dummy test case - no fenv.h support");
370 1.1 martin }
371 1.1 martin
372 1.1 martin
373 1.1 martin ATF_TC_BODY(t_nofenv, tc)
374 1.1 martin {
375 1.1 martin atf_tc_skip("no fenv.h support on this architecture");
376 1.1 martin }
377 1.1 martin
378 1.1 martin ATF_TP_ADD_TCS(tp)
379 1.1 martin {
380 1.1 martin ATF_TP_ADD_TC(tp, t_nofenv);
381 1.1 martin return atf_no_error();
382 1.1 martin }
383 1.1 martin
384 1.1 martin #endif
385