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