fenv.c revision 1.5
1/* $OpenBSD: fenv.c,v 1.5 2013/01/05 11:20:55 miod Exp $ */ 2 3/* 4 * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <fenv.h> 20 21/* 22 * The following constant represents the default floating-point environment 23 * (that is, the one installed at program startup) and has type pointer to 24 * const-qualified fenv_t. 25 * 26 * It can be used as an argument to the functions within the <fenv.h> header 27 * that manage the floating-point environment, namely fesetenv() and 28 * feupdateenv(). 29 */ 30fenv_t __fe_dfl_env = { 31 0x00000000, /* Control register */ 32 0x00000000 /* Status register */ 33}; 34 35/* 36 * The feclearexcept() function clears the supported floating-point exceptions 37 * represented by `excepts'. 38 */ 39int 40feclearexcept(int excepts) 41{ 42 unsigned int fpsr; 43 44 excepts &= FE_ALL_EXCEPT; 45 46 /* Store the current floating-point status register */ 47 __asm__ __volatile__ ("fldcr %0, %%fcr62" : "=r" (fpsr)); 48 49 /* Clear the requested floating-point exceptions */ 50 fpsr &= ~excepts; 51 52 /* Load the floating-point status register */ 53 __asm__ __volatile__ ("fstcr %0, %%fcr62" : : "r" (fpsr)); 54 55 return (0); 56} 57 58/* 59 * The fegetexceptflag() function stores an implementation-defined 60 * representation of the states of the floating-point status flags indicated by 61 * the argument excepts in the object pointed to by the argument flagp. 62 */ 63int 64fegetexceptflag(fexcept_t *flagp, int excepts) 65{ 66 unsigned int fpsr; 67 68 excepts &= FE_ALL_EXCEPT; 69 70 /* Store the current floating-point status register */ 71 __asm__ __volatile__ ("fldcr %0, %%fcr62" : "=r" (fpsr)); 72 73 /* Store the results in flagp */ 74 *flagp = fpsr & excepts; 75 76 return (0); 77} 78 79/* 80 * The feraiseexcept() function raises the supported floating-point exceptions 81 * represented by the argument `excepts'. 82 */ 83int 84feraiseexcept(int excepts) 85{ 86 volatile double d; 87 88 excepts &= FE_ALL_EXCEPT; 89 90 /* 91 * With a compiler that supports the FENV_ACCESS pragma 92 * properly, simple expressions like '0.0 / 0.0' should 93 * be sufficient to generate traps. Unfortunately, we 94 * need to bring a volatile variable into the equation 95 * to prevent incorrect optimizations. 96 */ 97 if (excepts & FE_INVALID) { 98 d = 0.0; 99 d = 0.0 / d; 100 } 101 if (excepts & FE_DIVBYZERO) { 102 d = 0.0; 103 d = 1.0 / d; 104 } 105 if (excepts & FE_OVERFLOW) { 106 d = 0x1.ffp1023; 107 d *= 2.0; 108 } 109 if (excepts & FE_UNDERFLOW) { 110 d = 0x1p1023; 111 d = 0x1p-1022 / d; 112 } 113 if (excepts & FE_INEXACT) { 114 d = 0x1p-1022; 115 d += 1.0; 116 } 117 return (0); 118} 119 120/* 121 * This function sets the floating-point status flags indicated by the argument 122 * `excepts' to the states stored in the object pointed to by `flagp'. It does 123 * NOT raise any floating-point exceptions, but only sets the state of the flags. 124 */ 125int 126fesetexceptflag(const fexcept_t *flagp, int excepts) 127{ 128 unsigned int fpsr; 129 130 excepts &= FE_ALL_EXCEPT; 131 132 /* Store the current floating-point status register */ 133 __asm__ __volatile__ ("fldcr %0, %%fcr62" : "=r" (fpsr)); 134 135 /* Set the requested status flags */ 136 fpsr &= ~excepts; 137 fpsr |= *flagp & excepts; 138 139 /* Load the floating-point status register */ 140 __asm__ __volatile__ ("fstcr %0, %%fcr62" : : "r" (fpsr)); 141 142 return (0); 143} 144 145/* 146 * The fetestexcept() function determines which of a specified subset of the 147 * floating-point exception flags are currently set. The `excepts' argument 148 * specifies the floating-point status flags to be queried. 149 */ 150int 151fetestexcept(int excepts) 152{ 153 unsigned int fpsr; 154 155 excepts &= FE_ALL_EXCEPT; 156 157 /* Store the current floating-point status register */ 158 __asm__ __volatile__ ("fldcr %0, %%fcr62" : "=r" (fpsr)); 159 160 return (fpsr & excepts); 161} 162 163/* 164 * The fegetround() function gets the current rounding direction. 165 */ 166int 167fegetround(void) 168{ 169 unsigned int fpcr; 170 171 /* Store the current floating-point control register */ 172 __asm__ __volatile__ ("fldcr %0, %%fcr63" : "=r" (fpcr)); 173 174 return (fpcr & _ROUND_MASK); 175} 176 177/* 178 * The fesetround() function establishes the rounding direction represented by 179 * its argument `round'. If the argument is not equal to the value of a rounding 180 * direction macro, the rounding direction is not changed. 181 */ 182int 183fesetround(int round) 184{ 185 unsigned int fpcr; 186 187 /* Check whether requested rounding direction is supported */ 188 if (round & ~_ROUND_MASK) 189 return (-1); 190 191 /* Store the current floating-point control register */ 192 __asm__ __volatile__ ("fldcr %0, %%fcr63" : "=r" (fpcr)); 193 194 /* Set the rounding direction */ 195 fpcr &= ~_ROUND_MASK; 196 fpcr |= round; 197 198 /* Load the floating-point control register */ 199 __asm__ __volatile__ ("fstcr %0, %%fcr63" : : "r" (fpcr)); 200 201 return (0); 202} 203 204/* 205 * The fegetenv() function attempts to store the current floating-point 206 * environment in the object pointed to by envp. 207 */ 208int 209fegetenv(fenv_t *envp) 210{ 211 /* Store the current floating-point control and status registers */ 212 __asm__ __volatile__ ("fldcr %0, %%fcr63" : "=r" (envp->__control)); 213 __asm__ __volatile__ ("fldcr %0, %%fcr62" : "=r" (envp->__status)); 214 215 return (0); 216} 217 218/* 219 * The feholdexcept() function saves the current floating-point environment 220 * in the object pointed to by envp, clears the floating-point status flags, and 221 * then installs a non-stop (continue on floating-point exceptions) mode, if 222 * available, for all floating-point exceptions. 223 */ 224int 225feholdexcept(fenv_t *envp) 226{ 227 unsigned int fpsr, fpcr; 228 229 /* Store the current floating-point control and status registers */ 230 __asm__ __volatile__ ("fldcr %0, %%fcr63" : "=r" (envp->__control)); 231 __asm__ __volatile__ ("fldcr %0, %%fcr62" : "=r" (envp->__status)); 232 233 /* Clear exception flags in FPSR */ 234 fpsr = envp->__status; 235 fpsr &= ~FE_ALL_EXCEPT; 236 __asm__ __volatile__ ("fstcr %0, %%fcr62" : : "r" (fpsr)); 237 238 /* Mask all exceptions */ 239 fpcr = envp->__control; 240 fpcr &= ~FE_ALL_EXCEPT; 241 __asm__ __volatile__ ("fstcr %0, %%fcr63" : : "r" (fpcr)); 242 243 return (0); 244} 245 246/* 247 * The fesetenv() function attempts to establish the floating-point environment 248 * represented by the object pointed to by envp. The argument `envp' points 249 * to an object set by a call to fegetenv() or feholdexcept(), or equal a 250 * floating-point environment macro. The fesetenv() function does not raise 251 * floating-point exceptions, but only installs the state of the floating-point 252 * status flags represented through its argument. 253 */ 254int 255fesetenv(const fenv_t *envp) 256{ 257 fenv_t fenv; 258 259 /* Store the current floating-point control and status registers */ 260 __asm__ __volatile__ ("fldcr %0, %%fcr63" : "=r" (fenv.__control)); 261 __asm__ __volatile__ ("fldcr %0, %%fcr62" : "=r" (fenv.__status)); 262 263 /* Set the requested control flags */ 264 fenv.__control &= ~(FE_ALL_EXCEPT | _ROUND_MASK); 265 fenv.__control |= envp->__control & (FE_ALL_EXCEPT | _ROUND_MASK); 266 267 /* Set the requested status flags */ 268 fenv.__status &= ~FE_ALL_EXCEPT; 269 fenv.__status |= envp->__status & FE_ALL_EXCEPT; 270 271 /* Load the floating-point control and status registers */ 272 __asm__ __volatile__ ("fstcr %0, %%fcr63" : : "r" (fenv.__control)); 273 __asm__ __volatile__ ("fstcr %0, %%fcr62" : : "r" (fenv.__status)); 274 275 return (0); 276} 277 278/* 279 * The feupdateenv() function saves the currently raised floating-point 280 * exceptions in its automatic storage, installs the floating-point environment 281 * represented by the object pointed to by `envp', and then raises the saved 282 * floating-point exceptions. The argument `envp' shall point to an object set 283 * by a call to feholdexcept() or fegetenv(), or equal a floating-point 284 * environment macro. 285 */ 286int 287feupdateenv(const fenv_t *envp) 288{ 289 unsigned int fpsr; 290 291 /* Store the current floating-point status register */ 292 __asm__ __volatile__ ("fldcr %0, %%fcr62" : "=r" (fpsr)); 293 294 /* Install new floating-point environment */ 295 fesetenv(envp); 296 297 /* Raise any previously accumulated exceptions */ 298 feraiseexcept(fpsr); 299 300 return (0); 301} 302 303/* 304 * The following functions are extentions to the standard 305 */ 306int 307feenableexcept(int mask) 308{ 309 unsigned int fpcr, omask; 310 311 mask &= FE_ALL_EXCEPT; 312 313 /* Store the current floating-point control register */ 314 __asm__ __volatile__ ("fldcr %0, %%fcr63" : "=r" (fpcr)); 315 316 omask = fpcr & FE_ALL_EXCEPT; 317 fpcr |= mask; 318 319 /* Load the floating-point control register */ 320 __asm__ __volatile__ ("fstcr %0, %%fcr63" : : "r" (fpcr)); 321 322 return (omask); 323 324} 325 326int 327fedisableexcept(int mask) 328{ 329 unsigned int fpcr, omask; 330 331 mask &= FE_ALL_EXCEPT; 332 333 /* Store the current floating-point control register */ 334 __asm__ __volatile__ ("fldcr %0, %%fcr63" : "=r" (fpcr)); 335 336 omask = fpcr & FE_ALL_EXCEPT; 337 fpcr &= ~mask; 338 339 /* Load the floating-point control register */ 340 __asm__ __volatile__ ("fstcr %0, %%fcr63" : : "r" (fpcr)); 341 342 return (omask); 343} 344 345int 346fegetexcept(void) 347{ 348 unsigned int fpcr; 349 350 /* Store the current floating-point control register */ 351 __asm__ __volatile__ ("fldcr %0, %%fcr63" : "=r" (fpcr)); 352 353 return (fpcr & FE_ALL_EXCEPT); 354} 355