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