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