Blame lib/fpucw.h

Packit 8f70b4
/* Manipulating the FPU control word.  -*- coding: utf-8 -*-
Packit 8f70b4
   Copyright (C) 2007-2018 Free Software Foundation, Inc.
Packit 8f70b4
   Written by Bruno Haible <bruno@clisp.org>, 2007.
Packit 8f70b4
Packit 8f70b4
   This program is free software: you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
   (at your option) any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License
Packit 8f70b4
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
#ifndef _FPUCW_H
Packit 8f70b4
#define _FPUCW_H
Packit 8f70b4
Packit 8f70b4
/* The i386 floating point hardware (the 387 compatible FPU, not the modern
Packit 8f70b4
   SSE/SSE2 hardware) has a controllable rounding precision.  It is specified
Packit 8f70b4
   through the 'PC' bits in the FPU control word ('fctrl' register).  (See
Packit 8f70b4
   the GNU libc i386 <fpu_control.h> header for details.)
Packit 8f70b4
Packit 8f70b4
   On some platforms, such as Linux or Solaris, the default precision setting
Packit 8f70b4
   is set to "extended precision".  This means that 'long double' instructions
Packit 8f70b4
   operate correctly, but 'double' computations often produce slightly
Packit 8f70b4
   different results as on strictly IEEE 754 conforming systems.
Packit 8f70b4
Packit 8f70b4
   On some platforms, such as NetBSD, the default precision is set to
Packit 8f70b4
   "double precision".  This means that 'long double' instructions will operate
Packit 8f70b4
   only as 'double', i.e. lead to wrong results.  Similarly on FreeBSD 6.4, at
Packit 8f70b4
   least for the division of 'long double' numbers.
Packit 8f70b4
Packit 8f70b4
   The FPU control word is under control of the application, i.e. it is
Packit 8f70b4
   not required to be set either way by the ABI.  (In fact, the i386 ABI
Packit 8f70b4
   https://www.linux-mips.org/pub/linux/mips/doc/ABI/abi386-4.pdf page 3-12 = page 38
Packit 8f70b4
   is not clear about it.  But in any case, gcc treats the control word
Packit 8f70b4
   like a "preserved" register: it emits code that assumes that the control
Packit 8f70b4
   word is preserved across calls, and it restores the control word at the
Packit 8f70b4
   end of functions that modify it.)
Packit 8f70b4
Packit 8f70b4
   See Vincent Lefèvre's page https://www.vinc17.net/research/extended.en.html
Packit 8f70b4
   for a good explanation.
Packit 8f70b4
   See http://www.uwsg.iu.edu/hypermail/linux/kernel/0103.0/0453.html for
Packit 8f70b4
   some argumentation which setting should be the default.  */
Packit 8f70b4
Packit 8f70b4
/* This header file provides the following facilities:
Packit 8f70b4
     fpucw_t                        integral type holding the value of 'fctrl'
Packit 8f70b4
     FPU_PC_MASK                    bit mask denoting the precision control
Packit 8f70b4
     FPU_PC_DOUBLE                  precision control for 53 bits mantissa
Packit 8f70b4
     FPU_PC_EXTENDED                precision control for 64 bits mantissa
Packit 8f70b4
     GET_FPUCW ()                   yields the current FPU control word
Packit 8f70b4
     SET_FPUCW (word)               sets the FPU control word
Packit 8f70b4
     DECL_LONG_DOUBLE_ROUNDING      variable declaration for
Packit 8f70b4
                                    BEGIN/END_LONG_DOUBLE_ROUNDING
Packit 8f70b4
     BEGIN_LONG_DOUBLE_ROUNDING ()  starts a sequence of instructions with
Packit 8f70b4
                                    'long double' safe operation precision
Packit 8f70b4
     END_LONG_DOUBLE_ROUNDING ()    ends a sequence of instructions with
Packit 8f70b4
                                    'long double' safe operation precision
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
/* Inline assembler like this works only with GNU C.  */
Packit 8f70b4
#if (defined __i386__ || defined __x86_64__) && defined __GNUC__
Packit 8f70b4
Packit 8f70b4
typedef unsigned short fpucw_t; /* glibc calls this fpu_control_t */
Packit 8f70b4
Packit 8f70b4
# define FPU_PC_MASK 0x0300
Packit 8f70b4
# define FPU_PC_DOUBLE 0x200    /* glibc calls this _FPU_DOUBLE */
Packit 8f70b4
# define FPU_PC_EXTENDED 0x300  /* glibc calls this _FPU_EXTENDED */
Packit 8f70b4
Packit 8f70b4
# define GET_FPUCW() \
Packit 8f70b4
  ({ fpucw_t _cw;                                               \
Packit 8f70b4
     __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_cw));         \
Packit 8f70b4
     _cw;                                                       \
Packit 8f70b4
   })
Packit 8f70b4
# define SET_FPUCW(word) \
Packit 8f70b4
  (void)({ fpucw_t _ncw = (word);                               \
Packit 8f70b4
           __asm__ __volatile__ ("fldcw %0" : : "m" (*&_ncw));  \
Packit 8f70b4
         })
Packit 8f70b4
Packit 8f70b4
# define DECL_LONG_DOUBLE_ROUNDING \
Packit 8f70b4
  fpucw_t oldcw;
Packit 8f70b4
# define BEGIN_LONG_DOUBLE_ROUNDING() \
Packit 8f70b4
  (void)(oldcw = GET_FPUCW (),                                  \
Packit 8f70b4
         SET_FPUCW ((oldcw & ~FPU_PC_MASK) | FPU_PC_EXTENDED))
Packit 8f70b4
# define END_LONG_DOUBLE_ROUNDING() \
Packit 8f70b4
  SET_FPUCW (oldcw)
Packit 8f70b4
Packit 8f70b4
#else
Packit 8f70b4
Packit 8f70b4
typedef unsigned int fpucw_t;
Packit 8f70b4
Packit 8f70b4
# define FPU_PC_MASK 0
Packit 8f70b4
# define FPU_PC_DOUBLE 0
Packit 8f70b4
# define FPU_PC_EXTENDED 0
Packit 8f70b4
Packit 8f70b4
# define GET_FPUCW() 0
Packit 8f70b4
# define SET_FPUCW(word) (void)(word)
Packit 8f70b4
Packit 8f70b4
# define DECL_LONG_DOUBLE_ROUNDING
Packit 8f70b4
# define BEGIN_LONG_DOUBLE_ROUNDING()
Packit 8f70b4
# define END_LONG_DOUBLE_ROUNDING()
Packit 8f70b4
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#endif /* _FPUCW_H */