1 /* Manipulating the FPU control word. -*- coding: utf-8 -*- 2 Copyright (C) 2007-2021 Free Software Foundation, Inc. 3 Written by Bruno Haible <bruno@clisp.org>, 2007. 4 5 This file is free software: you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1 of the 8 License, or (at your option) any later version. 9 10 This file is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 17 18 #ifndef _FPUCW_H 19 #define _FPUCW_H 20 21 /* The i386 floating point hardware (the 387 compatible FPU, not the modern 22 SSE/SSE2 hardware) has a controllable rounding precision. It is specified 23 through the 'PC' bits in the FPU control word ('fctrl' register). (See 24 the GNU libc i386 <fpu_control.h> header for details.) 25 26 On some platforms, such as Linux or Solaris, the default precision setting 27 is set to "extended precision". This means that 'long double' instructions 28 operate correctly, but 'double' computations often produce slightly 29 different results as on strictly IEEE 754 conforming systems. 30 31 On some platforms, such as NetBSD, the default precision is set to 32 "double precision". This means that 'long double' instructions will operate 33 only as 'double', i.e. lead to wrong results. Similarly on FreeBSD 6.4, at 34 least for the division of 'long double' numbers. 35 36 The FPU control word is under control of the application, i.e. it is 37 not required to be set either way by the ABI. (In fact, the i386 ABI 38 https://www.linux-mips.org/pub/linux/mips/doc/ABI/abi386-4.pdf page 3-12 = page 38 39 is not clear about it. But in any case, gcc treats the control word 40 like a "preserved" register: it emits code that assumes that the control 41 word is preserved across calls, and it restores the control word at the 42 end of functions that modify it.) 43 44 See Vincent Lefèvre's page https://www.vinc17.net/research/extended.en.html 45 for a good explanation. 46 See https://web.archive.org/web/20060905133417/http://www.uwsg.iu.edu/hypermail/linux/kernel/0103.0/0453.html 47 some argumentation which setting should be the default. */ 48 49 /* This header file provides the following facilities: 50 fpucw_t integral type holding the value of 'fctrl' 51 FPU_PC_MASK bit mask denoting the precision control 52 FPU_PC_DOUBLE precision control for 53 bits mantissa 53 FPU_PC_EXTENDED precision control for 64 bits mantissa 54 GET_FPUCW () yields the current FPU control word 55 SET_FPUCW (word) sets the FPU control word 56 DECL_LONG_DOUBLE_ROUNDING variable declaration for 57 BEGIN/END_LONG_DOUBLE_ROUNDING 58 BEGIN_LONG_DOUBLE_ROUNDING () starts a sequence of instructions with 59 'long double' safe operation precision 60 END_LONG_DOUBLE_ROUNDING () ends a sequence of instructions with 61 'long double' safe operation precision 62 */ 63 64 /* Inline assembler like this works only with GNU C and clang. */ 65 #if (defined __i386__ || defined __x86_64__) && (defined __GNUC__ || defined __clang__) 66 67 typedef unsigned short fpucw_t; /* glibc calls this fpu_control_t */ 68 69 # define FPU_PC_MASK 0x0300 70 # define FPU_PC_DOUBLE 0x200 /* glibc calls this _FPU_DOUBLE */ 71 # define FPU_PC_EXTENDED 0x300 /* glibc calls this _FPU_EXTENDED */ 72 73 # define GET_FPUCW() __extension__ \ 74 ({ fpucw_t _cw; \ 75 __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_cw)); \ 76 _cw; \ 77 }) 78 # define SET_FPUCW(word) __extension__ \ 79 (void)({ fpucw_t _ncw = (word); \ 80 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_ncw)); \ 81 }) 82 83 # define DECL_LONG_DOUBLE_ROUNDING \ 84 fpucw_t oldcw; 85 # define BEGIN_LONG_DOUBLE_ROUNDING() \ 86 (void)(oldcw = GET_FPUCW (), \ 87 SET_FPUCW ((oldcw & ~FPU_PC_MASK) | FPU_PC_EXTENDED)) 88 # define END_LONG_DOUBLE_ROUNDING() \ 89 SET_FPUCW (oldcw) 90 91 #else 92 93 typedef unsigned int fpucw_t; 94 95 # define FPU_PC_MASK 0 96 # define FPU_PC_DOUBLE 0 97 # define FPU_PC_EXTENDED 0 98 99 # define GET_FPUCW() 0 100 # define SET_FPUCW(word) (void)(word) 101 102 # define DECL_LONG_DOUBLE_ROUNDING 103 # define BEGIN_LONG_DOUBLE_ROUNDING() 104 # define END_LONG_DOUBLE_ROUNDING() 105 106 #endif 107 108 #endif /* _FPUCW_H */