t_fenv.c revision 1.9 1 1.9 riastrad /* $NetBSD: t_fenv.c,v 1.9 2023/11/05 16:06:27 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.9 riastrad __RCSID("$NetBSD: t_fenv.c,v 1.9 2023/11/05 16:06:27 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.8 riastrad #include <float.h>
40 1.1 martin #include <ieeefp.h>
41 1.1 martin #include <stdlib.h>
42 1.1 martin
43 1.1 martin
44 1.4 martin #if (__arm__ && !__SOFTFP__) || __aarch64__
45 1.1 martin /*
46 1.6 kamil * Some NEON fpus do not trap on IEEE 754 FP exceptions.
47 1.5 martin * Skip these tests if running on them and compiled for
48 1.1 martin * hard float.
49 1.1 martin */
50 1.2 martin #define FPU_EXC_PREREQ() \
51 1.1 martin if (0 == fpsetmask(fpsetmask(FP_X_INV))) \
52 1.5 martin atf_tc_skip("FPU does not implement traps on FP exceptions");
53 1.2 martin
54 1.2 martin /*
55 1.2 martin * Same as above: some don't allow configuring the rounding mode.
56 1.2 martin */
57 1.2 martin #define FPU_RND_PREREQ() \
58 1.2 martin if (0 == fpsetround(fpsetround(FP_RZ))) \
59 1.2 martin atf_tc_skip("FPU does not implement configurable " \
60 1.2 martin "rounding modes");
61 1.1 martin #endif
62 1.1 martin
63 1.2 martin #ifndef FPU_EXC_PREREQ
64 1.2 martin #define FPU_EXC_PREREQ() /* nothing */
65 1.2 martin #endif
66 1.2 martin #ifndef FPU_RND_PREREQ
67 1.2 martin #define FPU_RND_PREREQ() /* nothing */
68 1.1 martin #endif
69 1.1 martin
70 1.1 martin
71 1.8 riastrad static int
72 1.8 riastrad feround_to_fltrounds(int feround)
73 1.8 riastrad {
74 1.8 riastrad
75 1.8 riastrad /*
76 1.8 riastrad * C99, Sec. 5.2.4.2.2 Characteristics of floating types
77 1.8 riastrad * <float.h>, p. 24, clause 7
78 1.8 riastrad */
79 1.8 riastrad switch (feround) {
80 1.8 riastrad case FE_TOWARDZERO:
81 1.8 riastrad return 0;
82 1.8 riastrad case FE_TONEAREST:
83 1.8 riastrad return 1;
84 1.8 riastrad case FE_UPWARD:
85 1.8 riastrad return 2;
86 1.8 riastrad case FE_DOWNWARD:
87 1.8 riastrad return 3;
88 1.8 riastrad default:
89 1.8 riastrad return -1;
90 1.8 riastrad }
91 1.8 riastrad }
92 1.8 riastrad
93 1.8 riastrad static void
94 1.8 riastrad checkfltrounds(void)
95 1.8 riastrad {
96 1.8 riastrad int feround = fegetround();
97 1.8 riastrad int expected = feround_to_fltrounds(feround);
98 1.8 riastrad
99 1.8 riastrad ATF_CHECK_EQ_MSG(FLT_ROUNDS, expected,
100 1.8 riastrad "FLT_ROUNDS=%d expected=%d fegetround()=%d",
101 1.8 riastrad FLT_ROUNDS, expected, feround);
102 1.8 riastrad }
103 1.8 riastrad
104 1.9 riastrad static void
105 1.9 riastrad checkrounding(int feround)
106 1.9 riastrad {
107 1.9 riastrad volatile double ulp1 = DBL_EPSILON;
108 1.9 riastrad double y1 = -1 + ulp1/4;
109 1.9 riastrad double y2 = 1 + 3*(ulp1/2);
110 1.9 riastrad
111 1.9 riastrad switch (feround) {
112 1.9 riastrad case FE_TONEAREST: {
113 1.9 riastrad double z1 = -1;
114 1.9 riastrad double z2 = 1 + 2*ulp1;
115 1.9 riastrad ATF_CHECK_EQ_MSG(y1, z1, "expected=%a actual=%a", y1, z1);
116 1.9 riastrad ATF_CHECK_EQ_MSG(y2, z2, "expected=%a actual=%a", y1, z2);
117 1.9 riastrad break;
118 1.9 riastrad }
119 1.9 riastrad case FE_TOWARDZERO: {
120 1.9 riastrad double z1 = -1 + ulp1/2;
121 1.9 riastrad double z2 = 1 + ulp1;
122 1.9 riastrad ATF_CHECK_EQ_MSG(y1, z1, "expected=%a actual=%a", y1, z1);
123 1.9 riastrad ATF_CHECK_EQ_MSG(y2, z2, "expected=%a actual=%a", y1, z2);
124 1.9 riastrad break;
125 1.9 riastrad }
126 1.9 riastrad case FE_UPWARD: {
127 1.9 riastrad double z1 = -1 + ulp1/2;
128 1.9 riastrad double z2 = 1 + 2*ulp1;
129 1.9 riastrad ATF_CHECK_EQ_MSG(y1, z1, "expected=%a actual=%a", y1, z1);
130 1.9 riastrad ATF_CHECK_EQ_MSG(y2, z2, "expected=%a actual=%a", y1, z2);
131 1.9 riastrad break;
132 1.9 riastrad }
133 1.9 riastrad case FE_DOWNWARD: {
134 1.9 riastrad double z1 = -1;
135 1.9 riastrad double z2 = 1 + ulp1;
136 1.9 riastrad ATF_CHECK_EQ_MSG(y1, z1, "expected=%a actual=%a", y1, z1);
137 1.9 riastrad ATF_CHECK_EQ_MSG(y2, z2, "expected=%a actual=%a", y1, z2);
138 1.9 riastrad break;
139 1.9 riastrad }
140 1.9 riastrad }
141 1.9 riastrad }
142 1.9 riastrad
143 1.1 martin ATF_TC(fegetround);
144 1.1 martin
145 1.1 martin ATF_TC_HEAD(fegetround, tc)
146 1.1 martin {
147 1.1 martin atf_tc_set_md_var(tc, "descr",
148 1.1 martin "verify the fegetround() function agrees with the legacy "
149 1.1 martin "fpsetround");
150 1.1 martin }
151 1.1 martin
152 1.1 martin ATF_TC_BODY(fegetround, tc)
153 1.1 martin {
154 1.2 martin FPU_RND_PREREQ();
155 1.2 martin
156 1.9 riastrad checkrounding(FE_TONEAREST);
157 1.9 riastrad
158 1.1 martin fpsetround(FP_RZ);
159 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_TOWARDZERO,
160 1.7 riastrad "fegetround()=%d FE_TOWARDZERO=%d",
161 1.7 riastrad fegetround(), FE_TOWARDZERO);
162 1.8 riastrad checkfltrounds();
163 1.9 riastrad checkrounding(FE_TOWARDZERO);
164 1.8 riastrad
165 1.1 martin fpsetround(FP_RM);
166 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_DOWNWARD,
167 1.7 riastrad "fegetround()=%d FE_DOWNWARD=%d",
168 1.7 riastrad fegetround(), FE_DOWNWARD);
169 1.8 riastrad checkfltrounds();
170 1.9 riastrad checkrounding(FE_DOWNWARD);
171 1.8 riastrad
172 1.1 martin fpsetround(FP_RN);
173 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_TONEAREST,
174 1.7 riastrad "fegetround()=%d FE_TONEAREST=%d",
175 1.7 riastrad fegetround(), FE_TONEAREST);
176 1.8 riastrad checkfltrounds();
177 1.9 riastrad checkrounding(FE_TONEAREST);
178 1.8 riastrad
179 1.1 martin fpsetround(FP_RP);
180 1.7 riastrad ATF_CHECK_EQ_MSG(fegetround(), FE_UPWARD,
181 1.7 riastrad "fegetround()=%d FE_UPWARD=%d",
182 1.7 riastrad fegetround(), FE_UPWARD);
183 1.8 riastrad checkfltrounds();
184 1.9 riastrad checkrounding(FE_UPWARD);
185 1.1 martin }
186 1.1 martin
187 1.1 martin ATF_TC(fesetround);
188 1.1 martin
189 1.1 martin ATF_TC_HEAD(fesetround, tc)
190 1.1 martin {
191 1.1 martin atf_tc_set_md_var(tc, "descr",
192 1.1 martin "verify the fesetround() function agrees with the legacy "
193 1.1 martin "fpgetround");
194 1.1 martin }
195 1.1 martin
196 1.1 martin ATF_TC_BODY(fesetround, tc)
197 1.1 martin {
198 1.2 martin FPU_RND_PREREQ();
199 1.2 martin
200 1.9 riastrad checkrounding(FE_TONEAREST);
201 1.9 riastrad
202 1.1 martin fesetround(FE_TOWARDZERO);
203 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RZ,
204 1.7 riastrad "fpgetround()=%d FP_RZ=%d",
205 1.7 riastrad (int)fpgetround(), (int)FP_RZ);
206 1.8 riastrad checkfltrounds();
207 1.9 riastrad checkrounding(FE_TOWARDZERO);
208 1.8 riastrad
209 1.1 martin fesetround(FE_DOWNWARD);
210 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RM,
211 1.7 riastrad "fpgetround()=%d FP_RM=%d",
212 1.7 riastrad (int)fpgetround(), (int)FP_RM);
213 1.8 riastrad checkfltrounds();
214 1.9 riastrad checkrounding(FE_DOWNWARD);
215 1.8 riastrad
216 1.1 martin fesetround(FE_TONEAREST);
217 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RN,
218 1.7 riastrad "fpgetround()=%d FP_RN=%d",
219 1.7 riastrad (int)fpgetround(), (int)FP_RN);
220 1.8 riastrad checkfltrounds();
221 1.9 riastrad checkrounding(FE_TONEAREST);
222 1.8 riastrad
223 1.1 martin fesetround(FE_UPWARD);
224 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetround(), FP_RP,
225 1.7 riastrad "fpgetround()=%d FP_RP=%d",
226 1.7 riastrad (int)fpgetround(), (int)FP_RP);
227 1.8 riastrad checkfltrounds();
228 1.9 riastrad checkrounding(FE_UPWARD);
229 1.1 martin }
230 1.1 martin
231 1.1 martin ATF_TC(fegetexcept);
232 1.1 martin
233 1.1 martin ATF_TC_HEAD(fegetexcept, tc)
234 1.1 martin {
235 1.1 martin atf_tc_set_md_var(tc, "descr",
236 1.1 martin "verify the fegetexcept() function agrees with the legacy "
237 1.1 martin "fpsetmask()");
238 1.1 martin }
239 1.1 martin
240 1.1 martin ATF_TC_BODY(fegetexcept, tc)
241 1.1 martin {
242 1.2 martin FPU_EXC_PREREQ();
243 1.1 martin
244 1.1 martin fpsetmask(0);
245 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), 0,
246 1.7 riastrad "fegetexcept()=%d",
247 1.7 riastrad fegetexcept());
248 1.1 martin
249 1.1 martin fpsetmask(FP_X_INV|FP_X_DZ|FP_X_OFL|FP_X_UFL|FP_X_IMP);
250 1.1 martin ATF_CHECK(fegetexcept() == (FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW
251 1.1 martin |FE_UNDERFLOW|FE_INEXACT));
252 1.1 martin
253 1.1 martin fpsetmask(FP_X_INV);
254 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_INVALID,
255 1.7 riastrad "fegetexcept()=%d FE_INVALID=%d",
256 1.7 riastrad fegetexcept(), FE_INVALID);
257 1.1 martin
258 1.1 martin fpsetmask(FP_X_DZ);
259 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_DIVBYZERO,
260 1.7 riastrad "fegetexcept()=%d FE_DIVBYZERO=%d",
261 1.7 riastrad fegetexcept(), FE_DIVBYZERO);
262 1.1 martin
263 1.1 martin fpsetmask(FP_X_OFL);
264 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_OVERFLOW,
265 1.7 riastrad "fegetexcept()=%d FE_OVERFLOW=%d",
266 1.7 riastrad fegetexcept(), FE_OVERFLOW);
267 1.1 martin
268 1.1 martin fpsetmask(FP_X_UFL);
269 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_UNDERFLOW,
270 1.7 riastrad "fegetexcept()=%d FE_UNDERFLOW=%d",
271 1.7 riastrad fegetexcept(), FE_UNDERFLOW);
272 1.1 martin
273 1.1 martin fpsetmask(FP_X_IMP);
274 1.7 riastrad ATF_CHECK_EQ_MSG(fegetexcept(), FE_INEXACT,
275 1.7 riastrad "fegetexcept()=%d FE_INEXACT=%d",
276 1.7 riastrad fegetexcept(), FE_INEXACT);
277 1.1 martin }
278 1.1 martin
279 1.1 martin ATF_TC(feenableexcept);
280 1.1 martin
281 1.1 martin ATF_TC_HEAD(feenableexcept, tc)
282 1.1 martin {
283 1.1 martin atf_tc_set_md_var(tc, "descr",
284 1.1 martin "verify the feenableexcept() function agrees with the legacy "
285 1.1 martin "fpgetmask()");
286 1.1 martin }
287 1.1 martin
288 1.1 martin ATF_TC_BODY(feenableexcept, tc)
289 1.1 martin {
290 1.2 martin FPU_EXC_PREREQ();
291 1.1 martin
292 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
293 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), 0,
294 1.7 riastrad "fpgetmask()=%d",
295 1.7 riastrad (int)fpgetmask());
296 1.1 martin
297 1.1 martin feenableexcept(FE_UNDERFLOW);
298 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_UFL,
299 1.7 riastrad "fpgetmask()=%d FP_X_UFL=%d",
300 1.7 riastrad (int)fpgetmask(), (int)FP_X_UFL);
301 1.1 martin
302 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
303 1.1 martin feenableexcept(FE_OVERFLOW);
304 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_OFL,
305 1.7 riastrad "fpgetmask()=%d FP_X_OFL=%d",
306 1.7 riastrad (int)fpgetmask(), (int)FP_X_OFL);
307 1.1 martin
308 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
309 1.1 martin feenableexcept(FE_DIVBYZERO);
310 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_DZ,
311 1.7 riastrad "fpgetmask()=%d FP_X_DZ=%d",
312 1.7 riastrad (int)fpgetmask(), (int)FP_X_DZ);
313 1.1 martin
314 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
315 1.1 martin feenableexcept(FE_INEXACT);
316 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_IMP,
317 1.7 riastrad "fpgetmask()=%d FP_X_IMP=%d",
318 1.7 riastrad (int)fpgetmask(), (int)FP_X_IMP);
319 1.1 martin
320 1.1 martin fedisableexcept(FE_ALL_EXCEPT);
321 1.1 martin feenableexcept(FE_INVALID);
322 1.7 riastrad ATF_CHECK_EQ_MSG(fpgetmask(), FP_X_INV,
323 1.7 riastrad "fpgetmask()=%d FP_X_INV=%d",
324 1.7 riastrad (int)fpgetmask(), (int)FP_X_INV);
325 1.1 martin }
326 1.1 martin
327 1.1 martin ATF_TP_ADD_TCS(tp)
328 1.1 martin {
329 1.1 martin ATF_TP_ADD_TC(tp, fegetround);
330 1.1 martin ATF_TP_ADD_TC(tp, fesetround);
331 1.1 martin ATF_TP_ADD_TC(tp, fegetexcept);
332 1.1 martin ATF_TP_ADD_TC(tp, feenableexcept);
333 1.1 martin
334 1.1 martin return atf_no_error();
335 1.1 martin }
336 1.1 martin
337 1.1 martin #else /* no fenv.h support */
338 1.1 martin
339 1.1 martin ATF_TC(t_nofenv);
340 1.1 martin
341 1.1 martin ATF_TC_HEAD(t_nofenv, tc)
342 1.1 martin {
343 1.1 martin atf_tc_set_md_var(tc, "descr",
344 1.1 martin "dummy test case - no fenv.h support");
345 1.1 martin }
346 1.1 martin
347 1.1 martin
348 1.1 martin ATF_TC_BODY(t_nofenv, tc)
349 1.1 martin {
350 1.1 martin atf_tc_skip("no fenv.h support on this architecture");
351 1.1 martin }
352 1.1 martin
353 1.1 martin ATF_TP_ADD_TCS(tp)
354 1.1 martin {
355 1.1 martin ATF_TP_ADD_TC(tp, t_nofenv);
356 1.1 martin return atf_no_error();
357 1.1 martin }
358 1.1 martin
359 1.1 martin #endif
360