fenv.c revision 1.6
13077Sache/* $OpenBSD: fenv.c,v 1.6 2022/12/27 17:10:07 jmc Exp $ */ 23077Sache 33077Sache/* 43077Sache * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org> 53077Sache * 63077Sache * Permission to use, copy, modify, and distribute this software for any 73077Sache * purpose with or without fee is hereby granted, provided that the above 83077Sache * copyright notice and this permission notice appear in all copies. 93077Sache * 103077Sache * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 113077Sache * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 123077Sache * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 133077Sache * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 143077Sache * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 153077Sache * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 163077Sache * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 173077Sache */ 183077Sache 193077Sache#include <fenv.h> 203077Sache 213077Sacheunion u { 223077Sache unsigned long long fpscr; 233077Sache unsigned int bits[2]; 243077Sache}; 2566834Sphk 2666834Sphk/* 273077Sache * The following constant represents the default floating-point environment 283077Sache * (that is, the one installed at program startup) and has type pointer to 293077Sache * const-qualified fenv_t. 3066834Sphk * 313077Sache * It can be used as an argument to the functions within the <fenv.h> header 323077Sache * that manage the floating-point environment, namely fesetenv() and 333077Sache * feupdateenv(). 343077Sache */ 353077Sachefenv_t __fe_dfl_env = 0; 363077Sache 373077Sache/* 383077Sache * The feclearexcept() function clears the supported floating-point exceptions 393077Sache * represented by `excepts'. 403077Sache */ 413077Sacheint 423077Sachefeclearexcept(int excepts) 433077Sache{ 443077Sache union u u; 453077Sache excepts &= FE_ALL_EXCEPT; 463077Sache 473077Sache /* Store the current floating-point status and control register */ 483077Sache __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 493077Sache 503077Sache /* Clear the requested floating-point exceptions */ 513077Sache u.bits[1] &= ~excepts; 523077Sache if (excepts & FE_INVALID) 53 u.bits[1] &= ~_FE_INVALID_ALL; 54 55 /* Load the floating-point status and control register */ 56 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr)); 57 58 return (0); 59} 60DEF_STD(feclearexcept); 61 62/* 63 * The fegetexceptflag() function stores an implementation-defined 64 * representation of the states of the floating-point status flags indicated by 65 * the argument excepts in the object pointed to by the argument flagp. 66 */ 67int 68fegetexceptflag(fexcept_t *flagp, int excepts) 69{ 70 union u u; 71 72 excepts &= FE_ALL_EXCEPT; 73 74 /* Store the current floating-point status and control register */ 75 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 76 77 /* Store the results in flagp */ 78 *flagp = u.bits[1] & excepts; 79 80 return (0); 81} 82 83/* 84 * The feraiseexcept() function raises the supported floating-point exceptions 85 * represented by the argument `excepts'. 86 */ 87int 88feraiseexcept(int excepts) 89{ 90 excepts &= FE_ALL_EXCEPT; 91 92 fesetexceptflag((fexcept_t *)&excepts, excepts); 93 94 return (0); 95} 96DEF_STD(feraiseexcept); 97 98/* 99 * This function sets the floating-point status flags indicated by the argument 100 * `excepts' to the states stored in the object pointed to by `flagp'. It does 101 * NOT raise any floating-point exceptions, but only sets the state of the flags. 102 */ 103int 104fesetexceptflag(const fexcept_t *flagp, int excepts) 105{ 106 union u u; 107 108 excepts &= FE_ALL_EXCEPT; 109 110 /* Store the current floating-point status and control register */ 111 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 112 113 /* Set the requested status flags */ 114 u.bits[1] &= ~excepts; 115 u.bits[1] |= *flagp & excepts; 116 if (excepts & FE_INVALID) { 117 if (*flagp & FE_INVALID) 118 u.bits[1] |= _FE_INVALID_SOFT; 119 else 120 u.bits[1] &= ~_FE_INVALID_ALL; 121 } 122 123 /* Load the floating-point status and control register */ 124 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr)); 125 126 return (0); 127} 128DEF_STD(fesetexceptflag); 129 130/* 131 * The fetestexcept() function determines which of a specified subset of the 132 * floating-point exception flags are currently set. The `excepts' argument 133 * specifies the floating-point status flags to be queried. 134 */ 135int 136fetestexcept(int excepts) 137{ 138 union u u; 139 140 excepts &= FE_ALL_EXCEPT; 141 142 /* Store the current floating-point status and control register */ 143 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 144 145 return (u.bits[1] & excepts); 146} 147DEF_STD(fetestexcept); 148 149/* 150 * The fegetround() function gets the current rounding direction. 151 */ 152int 153fegetround(void) 154{ 155 union u u; 156 157 /* Store the current floating-point status and control register */ 158 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 159 160 return (u.bits[1] & _ROUND_MASK); 161} 162DEF_STD(fegetround); 163 164/* 165 * The fesetround() function establishes the rounding direction represented by 166 * its argument `round'. If the argument is not equal to the value of a rounding 167 * direction macro, the rounding direction is not changed. 168 */ 169int 170fesetround(int round) 171{ 172 union u u; 173 174 /* Check whether requested rounding direction is supported */ 175 if (round & ~_ROUND_MASK) 176 return (-1); 177 178 /* Store the current floating-point status and control register */ 179 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 180 181 /* Set the rounding direction */ 182 u.bits[1] &= ~_ROUND_MASK; 183 u.bits[1] |= round; 184 185 /* Load the floating-point status and control register */ 186 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr)); 187 188 return (0); 189} 190DEF_STD(fesetround); 191 192/* 193 * The fegetenv() function attempts to store the current floating-point 194 * environment in the object pointed to by envp. 195 */ 196int 197fegetenv(fenv_t *envp) 198{ 199 union u u; 200 201 /* Store the current floating-point status and control register */ 202 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 203 204 *envp = u.bits[1]; 205 206 return (0); 207} 208DEF_STD(fegetenv); 209 210/* 211 * The feholdexcept() function saves the current floating-point environment 212 * in the object pointed to by envp, clears the floating-point status flags, and 213 * then installs a non-stop (continue on floating-point exceptions) mode, if 214 * available, for all floating-point exceptions. 215 */ 216int 217feholdexcept(fenv_t *envp) 218{ 219 union u u; 220 221 /* Store the current floating-point status and control register */ 222 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 223 224 *envp = u.bits[1]; 225 226 /* Clear exception flags in FPSCR */ 227 u.bits[1] &= ~(FE_ALL_EXCEPT | _FE_INVALID_ALL); 228 229 /* Mask all exceptions */ 230 u.bits[1] &= ~(FE_ALL_EXCEPT >> _MASK_SHIFT); 231 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr)); 232 233 return (0); 234} 235DEF_STD(feholdexcept); 236 237/* 238 * The fesetenv() function attempts to establish the floating-point environment 239 * represented by the object pointed to by envp. The argument `envp' points 240 * to an object set by a call to fegetenv() or feholdexcept(), or equal a 241 * floating-point environment macro. The fesetenv() function does not raise 242 * floating-point exceptions, but only installs the state of the floating-point 243 * status flags represented through its argument. 244 */ 245int 246fesetenv(const fenv_t *envp) 247{ 248 union u u; 249 250 u.bits[0] = 0; 251 u.bits[1] = *envp; 252 253 /* Load the floating-point status and control register */ 254 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr)); 255 256 return (0); 257} 258DEF_STD(fesetenv); 259 260/* 261 * The feupdateenv() function saves the currently raised floating-point 262 * exceptions in its automatic storage, installs the floating-point environment 263 * represented by the object pointed to by `envp', and then raises the saved 264 * floating-point exceptions. The argument `envp' shall point to an object set 265 * by a call to feholdexcept() or fegetenv(), or equal a floating-point 266 * environment macro. 267 */ 268int 269feupdateenv(const fenv_t *envp) 270{ 271 union u u; 272 273 /* Store the current floating-point status and control register */ 274 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 275 276 /* Install new floating-point environment */ 277 fesetenv(envp); 278 279 /* Raise any previously accumulated exceptions */ 280 feraiseexcept(u.bits[1]); 281 282 return (0); 283} 284DEF_STD(feupdateenv); 285 286/* 287 * The following functions are extensions to the standard 288 */ 289int 290feenableexcept(int mask) 291{ 292 union u u; 293 unsigned int omask; 294 295 mask &= FE_ALL_EXCEPT; 296 297 /* Store the current floating-point status and control register */ 298 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 299 300 omask = (u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT; 301 u.bits[1] |= mask >> _MASK_SHIFT; 302 303 /* Load the floating-point status and control register */ 304 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr)); 305 306 return (omask); 307 308} 309 310int 311fedisableexcept(int mask) 312{ 313 union u u; 314 unsigned int omask; 315 316 mask &= FE_ALL_EXCEPT; 317 318 /* Store the current floating-point status and control register */ 319 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 320 321 omask = (u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT; 322 u.bits[1] &= ~(mask >> _MASK_SHIFT); 323 324 /* Load the floating-point status and control register */ 325 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr)); 326 327 return (omask); 328} 329 330int 331fegetexcept(void) 332{ 333 union u u; 334 335 /* Store the current floating-point status and control register */ 336 __asm__ volatile ("mffs %0" : "=f" (u.fpscr)); 337 338 return ((u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT); 339} 340