Back to index

php5  5.3.10
zend_float.h
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | Zend Engine                                                          |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 2.00 of the Zend license,     |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.zend.com/license/2_00.txt.                                |
00011    | If you did not receive a copy of the Zend license and are unable to  |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@zend.com so we can mail you a copy immediately.              |
00014    +----------------------------------------------------------------------+
00015    | Authors: Christian Seiler <chris_se@gmx.net>                         |
00016    +----------------------------------------------------------------------+
00017 */
00018 
00019 /* $Id: zend_float.h 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #ifndef ZEND_FLOAT_H
00022 #define ZEND_FLOAT_H
00023 
00024 /*
00025   Define functions for FP initialization and de-initialization.
00026 */
00027 extern ZEND_API void zend_init_fpu(TSRMLS_D);
00028 extern ZEND_API void zend_shutdown_fpu(TSRMLS_D);
00029 extern ZEND_API void zend_ensure_fpu_mode(TSRMLS_D);
00030 
00031 /* Copy of the contents of xpfpa.h (which is under public domain)
00032    See http://wiki.php.net/rfc/rounding for details.
00033 
00034    Cross Platform Floating Point Arithmetics
00035 
00036    This header file defines several platform-dependent macros that ensure
00037    equal and deterministic floating point behaviour across several platforms,
00038    compilers and architectures.
00039 
00040    The current macros are currently only used on x86 and x86_64 architectures,
00041    on every other architecture, these macros expand to NOPs. This assumes that
00042    other architectures do not have an internal precision and the operhand types
00043    define the computational precision of floating point operations. This
00044    assumption may be false, in that case, the author is interested in further
00045    details on the other platform.
00046 
00047    For further details, please visit:
00048    http://www.christian-seiler.de/projekte/fpmath/
00049 
00050    Version: 20090317 */
00051 
00052 /*
00053  Implementation notes:
00054 
00055  x86_64:
00056   - Since all x86_64 compilers use SSE by default, it is probably unecessary
00057     to use these macros there. We define them anyway since we are too lazy
00058     to differentiate the architecture. Also, the compiler option -mfpmath=i387
00059     justifies this decision.
00060 
00061  General:
00062   - It would be nice if one could detect whether SSE if used for math via some
00063     funky compiler defines and if so, make the macros go to NOPs. Any ideas
00064     on how to do that?
00065 
00066  MS Visual C:
00067   - Since MSVC users tipically don't use autoconf or CMake, we will detect
00068     MSVC via compile time define.
00069 */
00070 
00071 /* MSVC detection (MSVC people usually don't use autoconf) */
00072 #ifdef _MSC_VER
00073 # if _MSC_VER >= 1500
00074    /* Visual C++ 2008 or higher, supports _controlfp_s */
00075 #  define HAVE__CONTROLFP_S
00076 # else
00077    /* Visual C++ (up to 2005), supports _controlfp */
00078 #  define HAVE__CONTROLFP
00079 # endif /* MSC_VER >= 1500 */
00080   /* Tell MSVC optimizer that we access FP environment */
00081 # if _MSC_VER >= 1500
00082 #  pragma fenv_access (on)
00083 # endif
00084 #endif /* _MSC_VER */
00085 
00086 #ifdef HAVE__CONTROLFP_S
00087 
00088 /* float.h defines _controlfp_s */
00089 # include <float.h>
00090 
00091 # define XPFPA_HAVE_CW 1
00092 # define XPFPA_CW_DATATYPE \
00093             unsigned int
00094 
00095 # define XPFPA_STORE_CW(vptr) do { \
00096             _controlfp_s((unsigned int *)(vptr), 0, 0); \
00097         } while (0)
00098 
00099 # define XPFPA_RESTORE_CW(vptr) do { \
00100             unsigned int _xpfpa_fpu_cw; \
00101             _controlfp_s(&_xpfpa_fpu_cw, *((unsigned int *)(vptr)), _MCW_PC); \
00102         } while (0)
00103 
00104 # define XPFPA_DECLARE \
00105             unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
00106 
00107 # define XPFPA_SWITCH_DOUBLE() do { \
00108             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
00109             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
00110             _controlfp_s(&_xpfpa_fpu_cw, _PC_53, _MCW_PC); \
00111         } while (0)
00112 # define XPFPA_SWITCH_SINGLE() do { \
00113             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
00114             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
00115             _controlfp_s(&_xpfpa_fpu_cw, _PC_24, _MCW_PC); \
00116         } while (0)
00117 /* NOTE: This only sets internal precision. MSVC does NOT support double-
00118    extended precision! */
00119 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
00120             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
00121             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
00122             _controlfp_s(&_xpfpa_fpu_cw, _PC_64, _MCW_PC); \
00123         } while (0)
00124 # define XPFPA_RESTORE() \
00125             _controlfp_s(&_xpfpa_fpu_cw, _xpfpa_fpu_oldcw, _MCW_PC)
00126 /* We do NOT use the volatile return trick since _controlfp_s is a function
00127    call and thus FP registers are saved in memory anyway. However, we do use
00128    a variable to ensure that the expression passed into val will be evaluated
00129    *before* switching back contexts. */
00130 # define XPFPA_RETURN_DOUBLE(val) \
00131             do { \
00132                 double _xpfpa_result = (val); \
00133                 XPFPA_RESTORE(); \
00134                 return _xpfpa_result; \
00135             } while (0)
00136 # define XPFPA_RETURN_SINGLE(val) \
00137             do { \
00138                 float _xpfpa_result = (val); \
00139                 XPFPA_RESTORE(); \
00140                 return _xpfpa_result; \
00141             } while (0)
00142 /* This won't work, but we add a macro for it anyway. */
00143 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
00144             do { \
00145                 long double _xpfpa_result = (val); \
00146                 XPFPA_RESTORE(); \
00147                 return _xpfpa_result; \
00148             } while (0)
00149 
00150 #elif defined(HAVE__CONTROLFP)
00151 
00152 /* float.h defines _controlfp */
00153 # include <float.h>
00154 
00155 # define XPFPA_DECLARE \
00156             unsigned int _xpfpa_fpu_oldcw;
00157 
00158 # define XPFPA_HAVE_CW 1
00159 # define XPFPA_CW_DATATYPE \
00160             unsigned int
00161 
00162 # define XPFPA_STORE_CW(vptr) do { \
00163             *((unsigned int *)(vptr)) = _controlfp(0, 0); \
00164         } while (0)
00165 
00166 # define XPFPA_RESTORE_CW(vptr) do { \
00167             _controlfp(*((unsigned int *)(vptr)), _MCW_PC); \
00168         } while (0)
00169 
00170 # define XPFPA_SWITCH_DOUBLE() do { \
00171             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
00172             _controlfp(_PC_53, _MCW_PC); \
00173         } while (0)
00174 # define XPFPA_SWITCH_SINGLE() do { \
00175             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
00176             _controlfp(_PC_24, _MCW_PC); \
00177         } while (0)
00178 /* NOTE: This will only work as expected on MinGW. */
00179 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
00180             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
00181             _controlfp(_PC_64, _MCW_PC); \
00182         } while (0)
00183 # define XPFPA_RESTORE() \
00184             _controlfp(_xpfpa_fpu_oldcw, _MCW_PC)
00185 /* We do NOT use the volatile return trick since _controlfp is a function
00186    call and thus FP registers are saved in memory anyway. However, we do use
00187    a variable to ensure that the expression passed into val will be evaluated
00188    *before* switching back contexts. */
00189 # define XPFPA_RETURN_DOUBLE(val) \
00190             do { \
00191                 double _xpfpa_result = (val); \
00192                 XPFPA_RESTORE(); \
00193                 return _xpfpa_result; \
00194             } while (0)
00195 # define XPFPA_RETURN_SINGLE(val) \
00196             do { \
00197                 float _xpfpa_result = (val); \
00198                 XPFPA_RESTORE(); \
00199                 return _xpfpa_result; \
00200             } while (0)
00201 /* This will only work on MinGW */
00202 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
00203             do { \
00204                 long double _xpfpa_result = (val); \
00205                 XPFPA_RESTORE(); \
00206                 return _xpfpa_result; \
00207             } while (0)
00208 
00209 #elif defined(HAVE__FPU_SETCW) /* glibc systems */
00210 
00211 /* fpu_control.h defines _FPU_[GS]ETCW */
00212 # include <fpu_control.h>
00213 
00214 # define XPFPA_DECLARE \
00215             fpu_control_t _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
00216 
00217 # define XPFPA_HAVE_CW 1
00218 # define XPFPA_CW_DATATYPE \
00219             fpu_control_t
00220 
00221 # define XPFPA_STORE_CW(vptr) do { \
00222             _FPU_GETCW((*((fpu_control_t *)(vptr)))); \
00223         } while (0)
00224 
00225 # define XPFPA_RESTORE_CW(vptr) do { \
00226             _FPU_SETCW((*((fpu_control_t *)(vptr)))); \
00227         } while (0)
00228 
00229 # define XPFPA_SWITCH_DOUBLE() do { \
00230             _FPU_GETCW(_xpfpa_fpu_oldcw); \
00231             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE; \
00232             _FPU_SETCW(_xpfpa_fpu_cw); \
00233         } while (0)
00234 # define XPFPA_SWITCH_SINGLE() do { \
00235             _FPU_GETCW(_xpfpa_fpu_oldcw); \
00236             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_DOUBLE) | _FPU_SINGLE; \
00237             _FPU_SETCW(_xpfpa_fpu_cw); \
00238         } while (0)
00239 # define XPFPA_SWITCH_DOUBLE_EXTENDED()  do { \
00240             _FPU_GETCW(_xpfpa_fpu_oldcw); \
00241             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_SINGLE & ~_FPU_DOUBLE) | _FPU_EXTENDED; \
00242             _FPU_SETCW(_xpfpa_fpu_cw); \
00243         } while (0)
00244 # define XPFPA_RESTORE() \
00245             _FPU_SETCW(_xpfpa_fpu_oldcw)
00246 /* We use a temporary volatile variable (in a new block) in order to ensure
00247    that the optimizer does not mis-optimize the instructions. Also, a volatile
00248    variable ensures truncation to correct precision. */
00249 # define XPFPA_RETURN_DOUBLE(val) \
00250             do { \
00251                 volatile double _xpfpa_result = (val); \
00252                 XPFPA_RESTORE(); \
00253                 return _xpfpa_result; \
00254             } while (0)
00255 # define XPFPA_RETURN_SINGLE(val) \
00256             do { \
00257                 volatile float _xpfpa_result = (val); \
00258                 XPFPA_RESTORE(); \
00259                 return _xpfpa_result; \
00260             } while (0)
00261 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
00262             do { \
00263                 volatile long double _xpfpa_result = (val); \
00264                 XPFPA_RESTORE(); \
00265                 return _xpfpa_result; \
00266             } while (0)
00267 
00268 #elif defined(HAVE_FPSETPREC) /* FreeBSD */
00269 
00270 /* fpu_control.h defines _FPU_[GS]ETCW */
00271 # include <machine/ieeefp.h>
00272 
00273 # define XPFPA_DECLARE \
00274             fp_prec_t _xpfpa_fpu_oldprec;
00275 
00276 # define XPFPA_HAVE_CW 1
00277 # define XPFPA_CW_DATATYPE \
00278             fp_prec_t
00279 
00280 # define XPFPA_STORE_CW(vptr) do { \
00281             *((fp_prec_t *)(vptr)) = fpgetprec(); \
00282         } while (0)
00283 
00284 # define XPFPA_RESTORE_CW(vptr) do { \
00285             fpsetprec(*((fp_prec_t *)(vptr))); \
00286         } while (0)
00287 
00288 # define XPFPA_SWITCH_DOUBLE() do { \
00289             _xpfpa_fpu_oldprec = fpgetprec(); \
00290             fpsetprec(FP_PD); \
00291         } while (0)
00292 # define XPFPA_SWITCH_SINGLE() do { \
00293             _xpfpa_fpu_oldprec = fpgetprec(); \
00294             fpsetprec(FP_PS); \
00295         } while (0)
00296 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
00297             _xpfpa_fpu_oldprec = fpgetprec(); \
00298             fpsetprec(FP_PE); \
00299         } while (0)
00300 # define XPFPA_RESTORE() \
00301             fpsetprec(_xpfpa_fpu_oldprec)
00302 /* We use a temporary volatile variable (in a new block) in order to ensure
00303    that the optimizer does not mis-optimize the instructions. Also, a volatile
00304    variable ensures truncation to correct precision. */
00305 # define XPFPA_RETURN_DOUBLE(val) \
00306             do { \
00307                 volatile double _xpfpa_result = (val); \
00308                 XPFPA_RESTORE(); \
00309                 return _xpfpa_result; \
00310             } while (0)
00311 # define XPFPA_RETURN_SINGLE(val) \
00312             do { \
00313                 volatile float _xpfpa_result = (val); \
00314                 XPFPA_RESTORE(); \
00315                 return _xpfpa_result; \
00316             } while (0)
00317 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
00318             do { \
00319                 volatile long double _xpfpa_result = (val); \
00320                 XPFPA_RESTORE(); \
00321                 return _xpfpa_result; \
00322             } while (0)
00323 
00324 #elif defined(HAVE_FPU_INLINE_ASM_X86)
00325 
00326 /*
00327   Custom x86 inline assembler implementation.
00328 
00329   This implementation does not use predefined wrappers of the OS / compiler
00330   but rather uses x86/x87 inline assembler directly. Basic instructions:
00331 
00332   fnstcw - Store the FPU control word in a variable
00333   fldcw  - Load the FPU control word from a variable
00334 
00335   Bits (only bits 8 and 9 are relevant, bits 0 to 7 are for other things):
00336      0x0yy: Single precision
00337      0x1yy: Reserved
00338      0x2yy: Double precision
00339      0x3yy: Double-extended precision
00340 
00341   We use an unsigned int for the datatype. glibc sources add __mode__ (__HI__)
00342   attribute to it (HI stands for half-integer according to docs). It is unclear
00343   what the does exactly and how portable it is.
00344 
00345   The assembly syntax works with GNU CC, Intel CC and Sun CC.
00346 */
00347 
00348 # define XPFPA_DECLARE \
00349             unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
00350 
00351 # define XPFPA_HAVE_CW 1
00352 # define XPFPA_CW_DATATYPE \
00353             unsigned int
00354 
00355 # define XPFPA_STORE_CW(vptr) do { \
00356             __asm__ __volatile__ ("fnstcw %0" : "=m" (*((unsigned int *)(vptr)))); \
00357         } while (0)
00358 
00359 # define XPFPA_RESTORE_CW(vptr) do { \
00360             __asm__ __volatile__ ("fldcw %0" : : "m" (*((unsigned int *)(vptr)))); \
00361         } while (0)
00362 
00363 # define XPFPA_SWITCH_DOUBLE() do { \
00364             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
00365             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x100) | 0x200; \
00366             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
00367         } while (0)
00368 # define XPFPA_SWITCH_SINGLE() do { \
00369             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
00370             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x300); \
00371             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
00372         } while (0)
00373 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
00374             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
00375             _xpfpa_fpu_cw = _xpfpa_fpu_oldcw | 0x300; \
00376             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
00377         } while (0)
00378 # define XPFPA_RESTORE() \
00379             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_oldcw))
00380 /* We use a temporary volatile variable (in a new block) in order to ensure
00381    that the optimizer does not mis-optimize the instructions. Also, a volatile
00382    variable ensures truncation to correct precision. */
00383 # define XPFPA_RETURN_DOUBLE(val) \
00384             do { \
00385                 volatile double _xpfpa_result = (val); \
00386                 XPFPA_RESTORE(); \
00387                 return _xpfpa_result; \
00388             } while (0)
00389 # define XPFPA_RETURN_SINGLE(val) \
00390             do { \
00391                 volatile float _xpfpa_result = (val); \
00392                 XPFPA_RESTORE(); \
00393                 return _xpfpa_result; \
00394             } while (0)
00395 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
00396             do { \
00397                 volatile long double _xpfpa_result = (val); \
00398                 XPFPA_RESTORE(); \
00399                 return _xpfpa_result; \
00400             } while (0)
00401 
00402 #else /* FPU CONTROL */
00403 
00404 /*
00405   This is either not an x87 FPU or the inline assembly syntax was not
00406   recognized. In any case, default to NOPs for the macros and hope the
00407   generated code will behave as planned.
00408 */
00409 # define XPFPA_DECLARE                      /* NOP */
00410 # define XPFPA_HAVE_CW                      0
00411 # define XPFPA_CW_DATATYPE                  unsigned int
00412 # define XPFPA_STORE_CW(variable)           /* NOP */
00413 # define XPFPA_RESTORE_CW(variable)         /* NOP */
00414 # define XPFPA_SWITCH_DOUBLE()              /* NOP */
00415 # define XPFPA_SWITCH_SINGLE()              /* NOP */
00416 # define XPFPA_SWITCH_DOUBLE_EXTENDED()     /* NOP */
00417 # define XPFPA_RESTORE()                    /* NOP */
00418 # define XPFPA_RETURN_DOUBLE(val)           return (val)
00419 # define XPFPA_RETURN_SINGLE(val)           return (val)
00420 # define XPFPA_RETURN_DOUBLE_EXTENDED(val)  return (val)
00421 
00422 #endif /* FPU CONTROL */
00423 
00424 #endif