fenv.c revision 1.5 1 1.5 andvar /* $NetBSD: fenv.c,v 1.5 2021/09/03 21:54:59 andvar Exp $ */
2 1.1 martin
3 1.1 martin /*-
4 1.1 martin * Copyright (c) 2004-2005 David Schultz <das (at) FreeBSD.ORG>
5 1.1 martin * All rights reserved.
6 1.1 martin *
7 1.1 martin * Redistribution and use in source and binary forms, with or without
8 1.1 martin * modification, are permitted provided that the following conditions
9 1.1 martin * are met:
10 1.1 martin * 1. Redistributions of source code must retain the above copyright
11 1.1 martin * notice, this list of conditions and the following disclaimer.
12 1.1 martin * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 martin * notice, this list of conditions and the following disclaimer in the
14 1.1 martin * documentation and/or other materials provided with the distribution.
15 1.1 martin *
16 1.1 martin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 1.1 martin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 1.1 martin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 1.1 martin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 1.1 martin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 1.1 martin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 1.1 martin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 1.1 martin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 1.1 martin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 1.1 martin */
26 1.1 martin #include <sys/cdefs.h>
27 1.5 andvar __RCSID("$NetBSD: fenv.c,v 1.5 2021/09/03 21:54:59 andvar Exp $");
28 1.4 chs
29 1.4 chs #include "namespace.h"
30 1.1 martin
31 1.1 martin #include <assert.h>
32 1.1 martin #include <fenv.h>
33 1.1 martin
34 1.4 chs #ifdef __weak_alias
35 1.4 chs __weak_alias(feclearexcept,_feclearexcept)
36 1.4 chs __weak_alias(fedisableexcept,_fedisableexcept)
37 1.4 chs __weak_alias(feenableexcept,_feenableexcept)
38 1.4 chs __weak_alias(fegetenv,_fegetenv)
39 1.4 chs __weak_alias(fegetexcept,_fegetexcept)
40 1.4 chs __weak_alias(fegetexceptflag,_fegetexceptflag)
41 1.4 chs __weak_alias(fegetround,_fegetround)
42 1.4 chs __weak_alias(feholdexcept,_feholdexcept)
43 1.4 chs __weak_alias(feraiseexcept,_feraiseexcept)
44 1.4 chs __weak_alias(fesetenv,_fesetenv)
45 1.4 chs __weak_alias(fesetexceptflag,_fesetexceptflag)
46 1.4 chs __weak_alias(fesetround,_fesetround)
47 1.4 chs __weak_alias(fetestexcept,_fetestexcept)
48 1.4 chs __weak_alias(feupdateenv,_feupdateenv)
49 1.4 chs #endif
50 1.4 chs
51 1.1 martin /*
52 1.1 martin * Convert from exception flags (__BITS(27,32)) to exception enable bits
53 1.1 martin * (__BITS(5,0)) by right-shifting this much:
54 1.1 martin */
55 1.1 martin #define FE_FLAGS_SHIFT 27
56 1.1 martin
57 1.1 martin /*
58 1.1 martin * Mask all rounding mode bits
59 1.1 martin */
60 1.1 martin #define FE_ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \
61 1.1 martin FE_UPWARD | FE_TOWARDZERO)
62 1.1 martin
63 1.1 martin /* Load lower 32 bits from floating-point state register */
64 1.1 martin static inline uint32_t
65 1.1 martin readfpsr(void)
66 1.1 martin {
67 1.1 martin uint32_t rv;
68 1.1 martin
69 1.3 skrll __asm__ __volatile__ ("fstw %%fr0, 0(%1)" : "=m" (rv) : "r"(&rv));
70 1.1 martin return rv;
71 1.1 martin }
72 1.1 martin
73 1.1 martin /* Save floating-point state register */
74 1.1 martin static inline void
75 1.1 martin writefpsr(uint32_t val)
76 1.1 martin {
77 1.3 skrll __asm__ __volatile__("fldw 0(%1),%%fr0" : : "m" (val), "r"(&val));
78 1.1 martin }
79 1.1 martin
80 1.1 martin /*
81 1.1 martin * The feclearexcept() function clears the supported floating-point exceptions
82 1.1 martin * represented by `excepts'.
83 1.1 martin */
84 1.1 martin int
85 1.1 martin feclearexcept(int excepts)
86 1.1 martin {
87 1.1 martin fexcept_t r;
88 1.1 martin int ex;
89 1.1 martin
90 1.1 martin _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
91 1.1 martin
92 1.1 martin ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
93 1.1 martin
94 1.1 martin r = readfpsr();
95 1.1 martin r &= ~ex;
96 1.1 martin writefpsr(r);
97 1.1 martin
98 1.1 martin /* Success */
99 1.1 martin return 0;
100 1.1 martin }
101 1.1 martin
102 1.1 martin /*
103 1.1 martin * The fegetexceptflag() function stores an implementation-defined
104 1.1 martin * representation of the states of the floating-point status flags indicated
105 1.1 martin * by the argument excepts in the object pointed to by the argument flagp.
106 1.1 martin */
107 1.1 martin int
108 1.1 martin fegetexceptflag(fexcept_t *flagp, int excepts)
109 1.1 martin {
110 1.1 martin fexcept_t r;
111 1.1 martin int ex;
112 1.1 martin
113 1.1 martin _DIAGASSERT(flagp != NULL);
114 1.1 martin _DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
115 1.1 martin
116 1.1 martin ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
117 1.1 martin
118 1.1 martin r = readfpsr();
119 1.1 martin *flagp = (r & ex) >> FE_FLAGS_SHIFT;
120 1.1 martin
121 1.1 martin /* Success */
122 1.1 martin return 0;
123 1.1 martin }
124 1.1 martin
125 1.1 martin
126 1.1 martin /*
127 1.1 martin * This function sets the floating-point status flags indicated by the argument
128 1.1 martin * `excepts' to the states stored in the object pointed to by `flagp'. It does
129 1.1 martin * NOT raise any floating-point exceptions, but only sets the state of the flags.
130 1.1 martin */
131 1.1 martin int
132 1.1 martin fesetexceptflag(const fexcept_t *flagp, int excepts)
133 1.1 martin {
134 1.1 martin fexcept_t r;
135 1.1 martin int ex;
136 1.1 martin
137 1.1 martin _DIAGASSERT(flagp != NULL);
138 1.1 martin _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
139 1.1 martin
140 1.1 martin ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
141 1.1 martin
142 1.1 martin r = readfpsr();
143 1.1 martin r &= ~ex;
144 1.1 martin r |= (*flagp << FE_FLAGS_SHIFT) & ex;
145 1.1 martin writefpsr(r);
146 1.1 martin
147 1.1 martin /* Success */
148 1.1 martin return 0;
149 1.1 martin }
150 1.1 martin
151 1.1 martin /*
152 1.1 martin * The feraiseexcept() function raises the supported floating-point exceptions
153 1.1 martin * represented by the argument `excepts'.
154 1.1 martin *
155 1.1 martin * The order in which these floating-point exceptions are raised is unspecified
156 1.1 martin * (by the standard).
157 1.1 martin */
158 1.1 martin int
159 1.1 martin feraiseexcept(int excepts)
160 1.1 martin {
161 1.1 martin volatile double d;
162 1.1 martin int ex;
163 1.1 martin
164 1.1 martin _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
165 1.1 martin
166 1.1 martin ex = excepts & FE_ALL_EXCEPT;
167 1.1 martin
168 1.1 martin /*
169 1.1 martin * With a compiler that supports the FENV_ACCESS pragma properly, simple
170 1.1 martin * expressions like '0.0 / 0.0' should be sufficient to generate traps.
171 1.1 martin * Unfortunately, we need to bring a volatile variable into the equation
172 1.1 martin * to prevent incorrect optimizations.
173 1.1 martin */
174 1.1 martin if (ex & FE_INVALID) {
175 1.1 martin d = 0.0;
176 1.1 martin d = 0.0 / d;
177 1.1 martin }
178 1.1 martin if (ex & FE_DIVBYZERO) {
179 1.1 martin d = 0.0;
180 1.1 martin d = 1.0 / d;
181 1.1 martin }
182 1.1 martin if (ex & FE_OVERFLOW) {
183 1.1 martin d = 0x1.ffp1023;
184 1.1 martin d *= 2.0;
185 1.1 martin }
186 1.1 martin if (ex & FE_UNDERFLOW) {
187 1.1 martin d = 0x1p-1022;
188 1.1 martin d /= 0x1p1023;
189 1.1 martin }
190 1.1 martin if (ex & FE_INEXACT) {
191 1.1 martin d = 0x1p-1022;
192 1.1 martin d += 1.0;
193 1.1 martin }
194 1.1 martin
195 1.1 martin /* Success */
196 1.1 martin return 0;
197 1.1 martin }
198 1.1 martin
199 1.1 martin /*
200 1.1 martin * The fetestexcept() function determines which of a specified subset of the
201 1.1 martin * floating-point exception flags are currently set. The `excepts' argument
202 1.1 martin * specifies the floating-point status flags to be queried.
203 1.1 martin */
204 1.1 martin int
205 1.1 martin fetestexcept(int excepts)
206 1.1 martin {
207 1.1 martin fexcept_t r;
208 1.1 martin
209 1.1 martin _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
210 1.1 martin
211 1.1 martin r = readfpsr();
212 1.1 martin
213 1.1 martin return (r >> FE_FLAGS_SHIFT) & (excepts & FE_ALL_EXCEPT);
214 1.1 martin }
215 1.1 martin
216 1.1 martin /*
217 1.1 martin * The fegetround() function gets the current rounding direction.
218 1.1 martin */
219 1.1 martin int
220 1.1 martin fegetround(void)
221 1.1 martin {
222 1.1 martin fenv_t r;
223 1.1 martin
224 1.1 martin r = readfpsr();
225 1.1 martin
226 1.2 martin return r & FE_ROUND_MASK;
227 1.1 martin }
228 1.1 martin
229 1.1 martin /*
230 1.1 martin * The fesetround() function establishes the rounding direction represented by
231 1.1 martin * its argument `round'. If the argument is not equal to the value of a rounding
232 1.1 martin * direction macro, the rounding direction is not changed.
233 1.1 martin */
234 1.1 martin int
235 1.1 martin fesetround(int round)
236 1.1 martin {
237 1.1 martin fenv_t r;
238 1.1 martin
239 1.1 martin _DIAGASSERT((round & ~FE_ROUND_MASK) == 0);
240 1.1 martin if (round & ~FE_ROUND_MASK)
241 1.1 martin return -1;
242 1.1 martin
243 1.1 martin r = readfpsr();
244 1.2 martin r &= ~FE_ROUND_MASK;
245 1.2 martin r |= round;
246 1.1 martin writefpsr(r);
247 1.1 martin
248 1.1 martin /* Success */
249 1.1 martin return 0;
250 1.1 martin }
251 1.1 martin
252 1.1 martin /*
253 1.1 martin * The fegetenv() function attempts to store the current floating-point
254 1.1 martin * environment in the object pointed to by envp.
255 1.1 martin */
256 1.1 martin int
257 1.1 martin fegetenv(fenv_t *envp)
258 1.1 martin {
259 1.1 martin _DIAGASSERT(envp != NULL);
260 1.1 martin
261 1.1 martin *envp = readfpsr();
262 1.1 martin
263 1.1 martin /* Success */
264 1.1 martin return 0;
265 1.1 martin }
266 1.1 martin
267 1.1 martin
268 1.1 martin /*
269 1.1 martin * The feholdexcept() function saves the current floating-point environment
270 1.1 martin * in the object pointed to by envp, clears the floating-point status flags, and
271 1.1 martin * then installs a non-stop (continue on floating-point exceptions) mode, if
272 1.1 martin * available, for all floating-point exceptions.
273 1.1 martin */
274 1.1 martin int
275 1.1 martin feholdexcept(fenv_t *envp)
276 1.1 martin {
277 1.1 martin fenv_t r;
278 1.1 martin
279 1.1 martin _DIAGASSERT(envp != NULL);
280 1.1 martin
281 1.1 martin r = readfpsr();
282 1.1 martin *envp = r;
283 1.1 martin r &= ~FE_ALL_EXCEPT;
284 1.1 martin writefpsr(r);
285 1.1 martin
286 1.1 martin /* Success */
287 1.1 martin return 0;
288 1.1 martin }
289 1.1 martin
290 1.1 martin /*
291 1.1 martin * The fesetenv() function attempts to establish the floating-point environment
292 1.1 martin * represented by the object pointed to by envp. The argument `envp' points
293 1.1 martin * to an object set by a call to fegetenv() or feholdexcept(), or equal a
294 1.1 martin * floating-point environment macro. The fesetenv() function does not raise
295 1.1 martin * floating-point exceptions, but only installs the state of the floating-point
296 1.1 martin * status flags represented through its argument.
297 1.1 martin */
298 1.1 martin int
299 1.1 martin fesetenv(const fenv_t *envp)
300 1.1 martin {
301 1.1 martin _DIAGASSERT(envp != NULL);
302 1.1 martin
303 1.1 martin writefpsr(*envp);
304 1.1 martin
305 1.1 martin /* Success */
306 1.1 martin return 0;
307 1.1 martin }
308 1.1 martin
309 1.1 martin
310 1.1 martin /*
311 1.1 martin * The feupdateenv() function saves the currently raised floating-point
312 1.1 martin * exceptions in its automatic storage, installs the floating-point environment
313 1.1 martin * represented by the object pointed to by `envp', and then raises the saved
314 1.1 martin * floating-point exceptions. The argument `envp' shall point to an object set
315 1.1 martin * by a call to feholdexcept() or fegetenv(), or equal a floating-point
316 1.1 martin * environment macro.
317 1.1 martin */
318 1.1 martin int
319 1.1 martin feupdateenv(const fenv_t *envp)
320 1.1 martin {
321 1.1 martin fexcept_t r;
322 1.1 martin
323 1.1 martin _DIAGASSERT(envp != NULL);
324 1.1 martin
325 1.1 martin r = readfpsr();
326 1.1 martin writefpsr(*envp);
327 1.1 martin
328 1.1 martin _DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
329 1.1 martin feraiseexcept(r & FE_ALL_EXCEPT);
330 1.1 martin
331 1.1 martin /* Success */
332 1.1 martin return 0;
333 1.1 martin }
334 1.1 martin
335 1.1 martin /*
336 1.5 andvar * The following functions are extensions to the standard
337 1.1 martin */
338 1.1 martin int
339 1.1 martin feenableexcept(int mask)
340 1.1 martin {
341 1.1 martin fenv_t old_r, new_r;
342 1.1 martin
343 1.1 martin old_r = readfpsr();
344 1.1 martin new_r = old_r | (mask & FE_ALL_EXCEPT);
345 1.1 martin writefpsr(new_r);
346 1.1 martin
347 1.1 martin return old_r & FE_ALL_EXCEPT;
348 1.1 martin }
349 1.1 martin
350 1.1 martin int
351 1.1 martin fedisableexcept(int mask)
352 1.1 martin {
353 1.1 martin fenv_t old_r, new_r;
354 1.1 martin
355 1.1 martin old_r = readfpsr();
356 1.1 martin new_r = old_r & ~(mask & FE_ALL_EXCEPT);
357 1.1 martin writefpsr(new_r);
358 1.1 martin
359 1.1 martin return old_r & FE_ALL_EXCEPT;
360 1.1 martin }
361 1.1 martin
362 1.1 martin int
363 1.1 martin fegetexcept(void)
364 1.1 martin {
365 1.1 martin fenv_t r;
366 1.1 martin
367 1.1 martin r = readfpsr();
368 1.1 martin return r & FE_ALL_EXCEPT;
369 1.1 martin }
370