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