Home | History | Annotate | Download | only in lib
      1 /* Manipulating the FPU control word.
      2    Copyright (C) 2007-2012 Free Software Foundation, Inc.
      3    Written by Bruno Haible <bruno (at) clisp.org>, 2007.
      4 
      5    This program is free software: you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 3 of the License, or
      8    (at your option) any later version.
      9 
     10    This program 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 General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program.  If not, see <http://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    http://refspecs.freestandards.org/elf/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 Lefvre's page http://www.vinc17.org/research/extended.en.html
     45    for a good explanation.
     46    See http://www.uwsg.iu.edu/hypermail/linux/kernel/0103.0/0453.html for
     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.  */
     65 #if (defined __i386__ || defined __x86_64__) && defined __GNUC__
     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() \
     74   ({ fpucw_t _cw;                                               \
     75      __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_cw));         \
     76      _cw;                                                       \
     77    })
     78 # define SET_FPUCW(word) \
     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 */
    109