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