fenv.c revision 1.2 1 1.2 martin /* $NetBSD: fenv.c,v 1.2 2015/01/13 11:16:06 martin 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.2 martin __RCSID("$NetBSD: fenv.c,v 1.2 2015/01/13 11:16:06 martin Exp $");
28 1.1 martin
29 1.1 martin #include <assert.h>
30 1.1 martin #include <fenv.h>
31 1.1 martin
32 1.1 martin /*
33 1.1 martin * Convert from exception flags (__BITS(27,32)) to exception enable bits
34 1.1 martin * (__BITS(5,0)) by right-shifting this much:
35 1.1 martin */
36 1.1 martin #define FE_FLAGS_SHIFT 27
37 1.1 martin
38 1.1 martin /*
39 1.1 martin * Mask all rounding mode bits
40 1.1 martin */
41 1.1 martin #define FE_ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \
42 1.1 martin FE_UPWARD | FE_TOWARDZERO)
43 1.1 martin
44 1.1 martin /* Load lower 32 bits from floating-point state register */
45 1.1 martin static inline uint32_t
46 1.1 martin readfpsr(void)
47 1.1 martin {
48 1.1 martin uint32_t rv;
49 1.1 martin
50 1.1 martin __asm__ __volatile__ ("fstws %%fr0, %0" : "=m"(rv));
51 1.1 martin return rv;
52 1.1 martin }
53 1.1 martin
54 1.1 martin /* Save floating-point state register */
55 1.1 martin static inline void
56 1.1 martin writefpsr(uint32_t val)
57 1.1 martin {
58 1.1 martin __asm__ __volatile__("fldws %0,%%fr0" : : "m"(val));
59 1.1 martin }
60 1.1 martin
61 1.1 martin /*
62 1.1 martin * The feclearexcept() function clears the supported floating-point exceptions
63 1.1 martin * represented by `excepts'.
64 1.1 martin */
65 1.1 martin int
66 1.1 martin feclearexcept(int excepts)
67 1.1 martin {
68 1.1 martin fexcept_t r;
69 1.1 martin int ex;
70 1.1 martin
71 1.1 martin _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
72 1.1 martin
73 1.1 martin ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
74 1.1 martin
75 1.1 martin r = readfpsr();
76 1.1 martin r &= ~ex;
77 1.1 martin writefpsr(r);
78 1.1 martin
79 1.1 martin /* Success */
80 1.1 martin return 0;
81 1.1 martin }
82 1.1 martin
83 1.1 martin /*
84 1.1 martin * The fegetexceptflag() function stores an implementation-defined
85 1.1 martin * representation of the states of the floating-point status flags indicated
86 1.1 martin * by the argument excepts in the object pointed to by the argument flagp.
87 1.1 martin */
88 1.1 martin int
89 1.1 martin fegetexceptflag(fexcept_t *flagp, int excepts)
90 1.1 martin {
91 1.1 martin fexcept_t r;
92 1.1 martin int ex;
93 1.1 martin
94 1.1 martin _DIAGASSERT(flagp != NULL);
95 1.1 martin _DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
96 1.1 martin
97 1.1 martin ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
98 1.1 martin
99 1.1 martin r = readfpsr();
100 1.1 martin *flagp = (r & ex) >> FE_FLAGS_SHIFT;
101 1.1 martin
102 1.1 martin /* Success */
103 1.1 martin return 0;
104 1.1 martin }
105 1.1 martin
106 1.1 martin
107 1.1 martin /*
108 1.1 martin * This function sets the floating-point status flags indicated by the argument
109 1.1 martin * `excepts' to the states stored in the object pointed to by `flagp'. It does
110 1.1 martin * NOT raise any floating-point exceptions, but only sets the state of the flags.
111 1.1 martin */
112 1.1 martin int
113 1.1 martin fesetexceptflag(const fexcept_t *flagp, int excepts)
114 1.1 martin {
115 1.1 martin fexcept_t r;
116 1.1 martin int ex;
117 1.1 martin
118 1.1 martin _DIAGASSERT(flagp != NULL);
119 1.1 martin _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
120 1.1 martin
121 1.1 martin ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
122 1.1 martin
123 1.1 martin r = readfpsr();
124 1.1 martin r &= ~ex;
125 1.1 martin r |= (*flagp << FE_FLAGS_SHIFT) & ex;
126 1.1 martin writefpsr(r);
127 1.1 martin
128 1.1 martin /* Success */
129 1.1 martin return 0;
130 1.1 martin }
131 1.1 martin
132 1.1 martin /*
133 1.1 martin * The feraiseexcept() function raises the supported floating-point exceptions
134 1.1 martin * represented by the argument `excepts'.
135 1.1 martin *
136 1.1 martin * The order in which these floating-point exceptions are raised is unspecified
137 1.1 martin * (by the standard).
138 1.1 martin */
139 1.1 martin int
140 1.1 martin feraiseexcept(int excepts)
141 1.1 martin {
142 1.1 martin volatile double d;
143 1.1 martin int ex;
144 1.1 martin
145 1.1 martin _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
146 1.1 martin
147 1.1 martin ex = excepts & FE_ALL_EXCEPT;
148 1.1 martin
149 1.1 martin /*
150 1.1 martin * With a compiler that supports the FENV_ACCESS pragma properly, simple
151 1.1 martin * expressions like '0.0 / 0.0' should be sufficient to generate traps.
152 1.1 martin * Unfortunately, we need to bring a volatile variable into the equation
153 1.1 martin * to prevent incorrect optimizations.
154 1.1 martin */
155 1.1 martin if (ex & FE_INVALID) {
156 1.1 martin d = 0.0;
157 1.1 martin d = 0.0 / d;
158 1.1 martin }
159 1.1 martin if (ex & FE_DIVBYZERO) {
160 1.1 martin d = 0.0;
161 1.1 martin d = 1.0 / d;
162 1.1 martin }
163 1.1 martin if (ex & FE_OVERFLOW) {
164 1.1 martin d = 0x1.ffp1023;
165 1.1 martin d *= 2.0;
166 1.1 martin }
167 1.1 martin if (ex & FE_UNDERFLOW) {
168 1.1 martin d = 0x1p-1022;
169 1.1 martin d /= 0x1p1023;
170 1.1 martin }
171 1.1 martin if (ex & FE_INEXACT) {
172 1.1 martin d = 0x1p-1022;
173 1.1 martin d += 1.0;
174 1.1 martin }
175 1.1 martin
176 1.1 martin /* Success */
177 1.1 martin return 0;
178 1.1 martin }
179 1.1 martin
180 1.1 martin /*
181 1.1 martin * The fetestexcept() function determines which of a specified subset of the
182 1.1 martin * floating-point exception flags are currently set. The `excepts' argument
183 1.1 martin * specifies the floating-point status flags to be queried.
184 1.1 martin */
185 1.1 martin int
186 1.1 martin fetestexcept(int excepts)
187 1.1 martin {
188 1.1 martin fexcept_t r;
189 1.1 martin
190 1.1 martin _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
191 1.1 martin
192 1.1 martin r = readfpsr();
193 1.1 martin
194 1.1 martin return (r >> FE_FLAGS_SHIFT) & (excepts & FE_ALL_EXCEPT);
195 1.1 martin }
196 1.1 martin
197 1.1 martin /*
198 1.1 martin * The fegetround() function gets the current rounding direction.
199 1.1 martin */
200 1.1 martin int
201 1.1 martin fegetround(void)
202 1.1 martin {
203 1.1 martin fenv_t r;
204 1.1 martin
205 1.1 martin r = readfpsr();
206 1.1 martin
207 1.2 martin return r & FE_ROUND_MASK;
208 1.1 martin }
209 1.1 martin
210 1.1 martin /*
211 1.1 martin * The fesetround() function establishes the rounding direction represented by
212 1.1 martin * its argument `round'. If the argument is not equal to the value of a rounding
213 1.1 martin * direction macro, the rounding direction is not changed.
214 1.1 martin */
215 1.1 martin int
216 1.1 martin fesetround(int round)
217 1.1 martin {
218 1.1 martin fenv_t r;
219 1.1 martin
220 1.1 martin _DIAGASSERT((round & ~FE_ROUND_MASK) == 0);
221 1.1 martin if (round & ~FE_ROUND_MASK)
222 1.1 martin return -1;
223 1.1 martin
224 1.1 martin r = readfpsr();
225 1.2 martin r &= ~FE_ROUND_MASK;
226 1.2 martin r |= round;
227 1.1 martin writefpsr(r);
228 1.1 martin
229 1.1 martin /* Success */
230 1.1 martin return 0;
231 1.1 martin }
232 1.1 martin
233 1.1 martin /*
234 1.1 martin * The fegetenv() function attempts to store the current floating-point
235 1.1 martin * environment in the object pointed to by envp.
236 1.1 martin */
237 1.1 martin int
238 1.1 martin fegetenv(fenv_t *envp)
239 1.1 martin {
240 1.1 martin _DIAGASSERT(envp != NULL);
241 1.1 martin
242 1.1 martin *envp = readfpsr();
243 1.1 martin
244 1.1 martin /* Success */
245 1.1 martin return 0;
246 1.1 martin }
247 1.1 martin
248 1.1 martin
249 1.1 martin /*
250 1.1 martin * The feholdexcept() function saves the current floating-point environment
251 1.1 martin * in the object pointed to by envp, clears the floating-point status flags, and
252 1.1 martin * then installs a non-stop (continue on floating-point exceptions) mode, if
253 1.1 martin * available, for all floating-point exceptions.
254 1.1 martin */
255 1.1 martin int
256 1.1 martin feholdexcept(fenv_t *envp)
257 1.1 martin {
258 1.1 martin fenv_t r;
259 1.1 martin
260 1.1 martin _DIAGASSERT(envp != NULL);
261 1.1 martin
262 1.1 martin r = readfpsr();
263 1.1 martin *envp = r;
264 1.1 martin r &= ~FE_ALL_EXCEPT;
265 1.1 martin writefpsr(r);
266 1.1 martin
267 1.1 martin /* Success */
268 1.1 martin return 0;
269 1.1 martin }
270 1.1 martin
271 1.1 martin /*
272 1.1 martin * The fesetenv() function attempts to establish the floating-point environment
273 1.1 martin * represented by the object pointed to by envp. The argument `envp' points
274 1.1 martin * to an object set by a call to fegetenv() or feholdexcept(), or equal a
275 1.1 martin * floating-point environment macro. The fesetenv() function does not raise
276 1.1 martin * floating-point exceptions, but only installs the state of the floating-point
277 1.1 martin * status flags represented through its argument.
278 1.1 martin */
279 1.1 martin int
280 1.1 martin fesetenv(const fenv_t *envp)
281 1.1 martin {
282 1.1 martin _DIAGASSERT(envp != NULL);
283 1.1 martin
284 1.1 martin writefpsr(*envp);
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 /*
292 1.1 martin * The feupdateenv() function saves the currently raised floating-point
293 1.1 martin * exceptions in its automatic storage, installs the floating-point environment
294 1.1 martin * represented by the object pointed to by `envp', and then raises the saved
295 1.1 martin * floating-point exceptions. The argument `envp' shall point to an object set
296 1.1 martin * by a call to feholdexcept() or fegetenv(), or equal a floating-point
297 1.1 martin * environment macro.
298 1.1 martin */
299 1.1 martin int
300 1.1 martin feupdateenv(const fenv_t *envp)
301 1.1 martin {
302 1.1 martin fexcept_t r;
303 1.1 martin
304 1.1 martin _DIAGASSERT(envp != NULL);
305 1.1 martin
306 1.1 martin r = readfpsr();
307 1.1 martin writefpsr(*envp);
308 1.1 martin
309 1.1 martin _DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
310 1.1 martin feraiseexcept(r & FE_ALL_EXCEPT);
311 1.1 martin
312 1.1 martin /* Success */
313 1.1 martin return 0;
314 1.1 martin }
315 1.1 martin
316 1.1 martin /*
317 1.1 martin * The following functions are extentions to the standard
318 1.1 martin */
319 1.1 martin int
320 1.1 martin feenableexcept(int mask)
321 1.1 martin {
322 1.1 martin fenv_t old_r, new_r;
323 1.1 martin
324 1.1 martin old_r = readfpsr();
325 1.1 martin new_r = old_r | (mask & FE_ALL_EXCEPT);
326 1.1 martin writefpsr(new_r);
327 1.1 martin
328 1.1 martin return old_r & FE_ALL_EXCEPT;
329 1.1 martin }
330 1.1 martin
331 1.1 martin int
332 1.1 martin fedisableexcept(int mask)
333 1.1 martin {
334 1.1 martin fenv_t old_r, new_r;
335 1.1 martin
336 1.1 martin old_r = readfpsr();
337 1.1 martin new_r = old_r & ~(mask & FE_ALL_EXCEPT);
338 1.1 martin writefpsr(new_r);
339 1.1 martin
340 1.1 martin return old_r & FE_ALL_EXCEPT;
341 1.1 martin }
342 1.1 martin
343 1.1 martin int
344 1.1 martin fegetexcept(void)
345 1.1 martin {
346 1.1 martin fenv_t r;
347 1.1 martin
348 1.1 martin r = readfpsr();
349 1.1 martin return r & FE_ALL_EXCEPT;
350 1.1 martin }
351