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