xref: /PHP-5.3/Zend/zend_float.h (revision 831fbcf3)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2013 Zend Technologies Ltd. (http://www.zend.com) |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend license,     |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Christian Seiler <chris_se@gmx.net>                         |
16    +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #ifndef ZEND_FLOAT_H
22 #define ZEND_FLOAT_H
23 
24 /*
25   Define functions for FP initialization and de-initialization.
26 */
27 extern ZEND_API void zend_init_fpu(TSRMLS_D);
28 extern ZEND_API void zend_shutdown_fpu(TSRMLS_D);
29 extern ZEND_API void zend_ensure_fpu_mode(TSRMLS_D);
30 
31 /* Copy of the contents of xpfpa.h (which is under public domain)
32    See http://wiki.php.net/rfc/rounding for details.
33 
34    Cross Platform Floating Point Arithmetics
35 
36    This header file defines several platform-dependent macros that ensure
37    equal and deterministic floating point behaviour across several platforms,
38    compilers and architectures.
39 
40    The current macros are currently only used on x86 and x86_64 architectures,
41    on every other architecture, these macros expand to NOPs. This assumes that
42    other architectures do not have an internal precision and the operhand types
43    define the computational precision of floating point operations. This
44    assumption may be false, in that case, the author is interested in further
45    details on the other platform.
46 
47    For further details, please visit:
48    http://www.christian-seiler.de/projekte/fpmath/
49 
50    Version: 20090317 */
51 
52 /*
53  Implementation notes:
54 
55  x86_64:
56   - Since all x86_64 compilers use SSE by default, it is probably unecessary
57     to use these macros there. We define them anyway since we are too lazy
58     to differentiate the architecture. Also, the compiler option -mfpmath=i387
59     justifies this decision.
60 
61  General:
62   - It would be nice if one could detect whether SSE if used for math via some
63     funky compiler defines and if so, make the macros go to NOPs. Any ideas
64     on how to do that?
65 
66  MS Visual C:
67   - Since MSVC users tipically don't use autoconf or CMake, we will detect
68     MSVC via compile time define.
69 */
70 
71 /* MSVC detection (MSVC people usually don't use autoconf) */
72 #ifdef _MSC_VER
73 # if _MSC_VER >= 1500
74    /* Visual C++ 2008 or higher, supports _controlfp_s */
75 #  define HAVE__CONTROLFP_S
76 # else
77    /* Visual C++ (up to 2005), supports _controlfp */
78 #  define HAVE__CONTROLFP
79 # endif /* MSC_VER >= 1500 */
80   /* Tell MSVC optimizer that we access FP environment */
81 # if _MSC_VER >= 1500
82 #  pragma fenv_access (on)
83 # endif
84 #endif /* _MSC_VER */
85 
86 #ifdef HAVE__CONTROLFP_S
87 
88 /* float.h defines _controlfp_s */
89 # include <float.h>
90 
91 # define XPFPA_HAVE_CW 1
92 # define XPFPA_CW_DATATYPE \
93             unsigned int
94 
95 # define XPFPA_STORE_CW(vptr) do { \
96             _controlfp_s((unsigned int *)(vptr), 0, 0); \
97         } while (0)
98 
99 # define XPFPA_RESTORE_CW(vptr) do { \
100             unsigned int _xpfpa_fpu_cw; \
101             _controlfp_s(&_xpfpa_fpu_cw, *((unsigned int *)(vptr)), _MCW_PC); \
102         } while (0)
103 
104 # define XPFPA_DECLARE \
105             unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
106 
107 # define XPFPA_SWITCH_DOUBLE() do { \
108             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
109             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
110             _controlfp_s(&_xpfpa_fpu_cw, _PC_53, _MCW_PC); \
111         } while (0)
112 # define XPFPA_SWITCH_SINGLE() do { \
113             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
114             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
115             _controlfp_s(&_xpfpa_fpu_cw, _PC_24, _MCW_PC); \
116         } while (0)
117 /* NOTE: This only sets internal precision. MSVC does NOT support double-
118    extended precision! */
119 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
120             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
121             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
122             _controlfp_s(&_xpfpa_fpu_cw, _PC_64, _MCW_PC); \
123         } while (0)
124 # define XPFPA_RESTORE() \
125             _controlfp_s(&_xpfpa_fpu_cw, _xpfpa_fpu_oldcw, _MCW_PC)
126 /* We do NOT use the volatile return trick since _controlfp_s is a function
127    call and thus FP registers are saved in memory anyway. However, we do use
128    a variable to ensure that the expression passed into val will be evaluated
129    *before* switching back contexts. */
130 # define XPFPA_RETURN_DOUBLE(val) \
131             do { \
132                 double _xpfpa_result = (val); \
133                 XPFPA_RESTORE(); \
134                 return _xpfpa_result; \
135             } while (0)
136 # define XPFPA_RETURN_SINGLE(val) \
137             do { \
138                 float _xpfpa_result = (val); \
139                 XPFPA_RESTORE(); \
140                 return _xpfpa_result; \
141             } while (0)
142 /* This won't work, but we add a macro for it anyway. */
143 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
144             do { \
145                 long double _xpfpa_result = (val); \
146                 XPFPA_RESTORE(); \
147                 return _xpfpa_result; \
148             } while (0)
149 
150 #elif defined(HAVE__CONTROLFP)
151 
152 /* float.h defines _controlfp */
153 # include <float.h>
154 
155 # define XPFPA_DECLARE \
156             unsigned int _xpfpa_fpu_oldcw;
157 
158 # define XPFPA_HAVE_CW 1
159 # define XPFPA_CW_DATATYPE \
160             unsigned int
161 
162 # define XPFPA_STORE_CW(vptr) do { \
163             *((unsigned int *)(vptr)) = _controlfp(0, 0); \
164         } while (0)
165 
166 # define XPFPA_RESTORE_CW(vptr) do { \
167             _controlfp(*((unsigned int *)(vptr)), _MCW_PC); \
168         } while (0)
169 
170 # define XPFPA_SWITCH_DOUBLE() do { \
171             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
172             _controlfp(_PC_53, _MCW_PC); \
173         } while (0)
174 # define XPFPA_SWITCH_SINGLE() do { \
175             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
176             _controlfp(_PC_24, _MCW_PC); \
177         } while (0)
178 /* NOTE: This will only work as expected on MinGW. */
179 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
180             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
181             _controlfp(_PC_64, _MCW_PC); \
182         } while (0)
183 # define XPFPA_RESTORE() \
184             _controlfp(_xpfpa_fpu_oldcw, _MCW_PC)
185 /* We do NOT use the volatile return trick since _controlfp is a function
186    call and thus FP registers are saved in memory anyway. However, we do use
187    a variable to ensure that the expression passed into val will be evaluated
188    *before* switching back contexts. */
189 # define XPFPA_RETURN_DOUBLE(val) \
190             do { \
191                 double _xpfpa_result = (val); \
192                 XPFPA_RESTORE(); \
193                 return _xpfpa_result; \
194             } while (0)
195 # define XPFPA_RETURN_SINGLE(val) \
196             do { \
197                 float _xpfpa_result = (val); \
198                 XPFPA_RESTORE(); \
199                 return _xpfpa_result; \
200             } while (0)
201 /* This will only work on MinGW */
202 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
203             do { \
204                 long double _xpfpa_result = (val); \
205                 XPFPA_RESTORE(); \
206                 return _xpfpa_result; \
207             } while (0)
208 
209 #elif defined(HAVE__FPU_SETCW) /* glibc systems */
210 
211 /* fpu_control.h defines _FPU_[GS]ETCW */
212 # include <fpu_control.h>
213 
214 # define XPFPA_DECLARE \
215             fpu_control_t _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
216 
217 # define XPFPA_HAVE_CW 1
218 # define XPFPA_CW_DATATYPE \
219             fpu_control_t
220 
221 # define XPFPA_STORE_CW(vptr) do { \
222             _FPU_GETCW((*((fpu_control_t *)(vptr)))); \
223         } while (0)
224 
225 # define XPFPA_RESTORE_CW(vptr) do { \
226             _FPU_SETCW((*((fpu_control_t *)(vptr)))); \
227         } while (0)
228 
229 # define XPFPA_SWITCH_DOUBLE() do { \
230             _FPU_GETCW(_xpfpa_fpu_oldcw); \
231             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE; \
232             _FPU_SETCW(_xpfpa_fpu_cw); \
233         } while (0)
234 # define XPFPA_SWITCH_SINGLE() do { \
235             _FPU_GETCW(_xpfpa_fpu_oldcw); \
236             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_DOUBLE) | _FPU_SINGLE; \
237             _FPU_SETCW(_xpfpa_fpu_cw); \
238         } while (0)
239 # define XPFPA_SWITCH_DOUBLE_EXTENDED()  do { \
240             _FPU_GETCW(_xpfpa_fpu_oldcw); \
241             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_SINGLE & ~_FPU_DOUBLE) | _FPU_EXTENDED; \
242             _FPU_SETCW(_xpfpa_fpu_cw); \
243         } while (0)
244 # define XPFPA_RESTORE() \
245             _FPU_SETCW(_xpfpa_fpu_oldcw)
246 /* We use a temporary volatile variable (in a new block) in order to ensure
247    that the optimizer does not mis-optimize the instructions. Also, a volatile
248    variable ensures truncation to correct precision. */
249 # define XPFPA_RETURN_DOUBLE(val) \
250             do { \
251                 volatile double _xpfpa_result = (val); \
252                 XPFPA_RESTORE(); \
253                 return _xpfpa_result; \
254             } while (0)
255 # define XPFPA_RETURN_SINGLE(val) \
256             do { \
257                 volatile float _xpfpa_result = (val); \
258                 XPFPA_RESTORE(); \
259                 return _xpfpa_result; \
260             } while (0)
261 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
262             do { \
263                 volatile long double _xpfpa_result = (val); \
264                 XPFPA_RESTORE(); \
265                 return _xpfpa_result; \
266             } while (0)
267 
268 #elif defined(HAVE_FPSETPREC) /* FreeBSD */
269 
270 /* fpu_control.h defines _FPU_[GS]ETCW */
271 # include <machine/ieeefp.h>
272 
273 # define XPFPA_DECLARE \
274             fp_prec_t _xpfpa_fpu_oldprec;
275 
276 # define XPFPA_HAVE_CW 1
277 # define XPFPA_CW_DATATYPE \
278             fp_prec_t
279 
280 # define XPFPA_STORE_CW(vptr) do { \
281             *((fp_prec_t *)(vptr)) = fpgetprec(); \
282         } while (0)
283 
284 # define XPFPA_RESTORE_CW(vptr) do { \
285             fpsetprec(*((fp_prec_t *)(vptr))); \
286         } while (0)
287 
288 # define XPFPA_SWITCH_DOUBLE() do { \
289             _xpfpa_fpu_oldprec = fpgetprec(); \
290             fpsetprec(FP_PD); \
291         } while (0)
292 # define XPFPA_SWITCH_SINGLE() do { \
293             _xpfpa_fpu_oldprec = fpgetprec(); \
294             fpsetprec(FP_PS); \
295         } while (0)
296 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
297             _xpfpa_fpu_oldprec = fpgetprec(); \
298             fpsetprec(FP_PE); \
299         } while (0)
300 # define XPFPA_RESTORE() \
301             fpsetprec(_xpfpa_fpu_oldprec)
302 /* We use a temporary volatile variable (in a new block) in order to ensure
303    that the optimizer does not mis-optimize the instructions. Also, a volatile
304    variable ensures truncation to correct precision. */
305 # define XPFPA_RETURN_DOUBLE(val) \
306             do { \
307                 volatile double _xpfpa_result = (val); \
308                 XPFPA_RESTORE(); \
309                 return _xpfpa_result; \
310             } while (0)
311 # define XPFPA_RETURN_SINGLE(val) \
312             do { \
313                 volatile float _xpfpa_result = (val); \
314                 XPFPA_RESTORE(); \
315                 return _xpfpa_result; \
316             } while (0)
317 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
318             do { \
319                 volatile long double _xpfpa_result = (val); \
320                 XPFPA_RESTORE(); \
321                 return _xpfpa_result; \
322             } while (0)
323 
324 #elif defined(HAVE_FPU_INLINE_ASM_X86)
325 
326 /*
327   Custom x86 inline assembler implementation.
328 
329   This implementation does not use predefined wrappers of the OS / compiler
330   but rather uses x86/x87 inline assembler directly. Basic instructions:
331 
332   fnstcw - Store the FPU control word in a variable
333   fldcw  - Load the FPU control word from a variable
334 
335   Bits (only bits 8 and 9 are relevant, bits 0 to 7 are for other things):
336      0x0yy: Single precision
337      0x1yy: Reserved
338      0x2yy: Double precision
339      0x3yy: Double-extended precision
340 
341   We use an unsigned int for the datatype. glibc sources add __mode__ (__HI__)
342   attribute to it (HI stands for half-integer according to docs). It is unclear
343   what the does exactly and how portable it is.
344 
345   The assembly syntax works with GNU CC, Intel CC and Sun CC.
346 */
347 
348 # define XPFPA_DECLARE \
349             unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
350 
351 # define XPFPA_HAVE_CW 1
352 # define XPFPA_CW_DATATYPE \
353             unsigned int
354 
355 # define XPFPA_STORE_CW(vptr) do { \
356             __asm__ __volatile__ ("fnstcw %0" : "=m" (*((unsigned int *)(vptr)))); \
357         } while (0)
358 
359 # define XPFPA_RESTORE_CW(vptr) do { \
360             __asm__ __volatile__ ("fldcw %0" : : "m" (*((unsigned int *)(vptr)))); \
361         } while (0)
362 
363 # define XPFPA_SWITCH_DOUBLE() do { \
364             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
365             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x100) | 0x200; \
366             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
367         } while (0)
368 # define XPFPA_SWITCH_SINGLE() do { \
369             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
370             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x300); \
371             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
372         } while (0)
373 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
374             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
375             _xpfpa_fpu_cw = _xpfpa_fpu_oldcw | 0x300; \
376             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
377         } while (0)
378 # define XPFPA_RESTORE() \
379             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_oldcw))
380 /* We use a temporary volatile variable (in a new block) in order to ensure
381    that the optimizer does not mis-optimize the instructions. Also, a volatile
382    variable ensures truncation to correct precision. */
383 # define XPFPA_RETURN_DOUBLE(val) \
384             do { \
385                 volatile double _xpfpa_result = (val); \
386                 XPFPA_RESTORE(); \
387                 return _xpfpa_result; \
388             } while (0)
389 # define XPFPA_RETURN_SINGLE(val) \
390             do { \
391                 volatile float _xpfpa_result = (val); \
392                 XPFPA_RESTORE(); \
393                 return _xpfpa_result; \
394             } while (0)
395 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
396             do { \
397                 volatile long double _xpfpa_result = (val); \
398                 XPFPA_RESTORE(); \
399                 return _xpfpa_result; \
400             } while (0)
401 
402 #else /* FPU CONTROL */
403 
404 /*
405   This is either not an x87 FPU or the inline assembly syntax was not
406   recognized. In any case, default to NOPs for the macros and hope the
407   generated code will behave as planned.
408 */
409 # define XPFPA_DECLARE                      /* NOP */
410 # define XPFPA_HAVE_CW                      0
411 # define XPFPA_CW_DATATYPE                  unsigned int
412 # define XPFPA_STORE_CW(variable)           /* NOP */
413 # define XPFPA_RESTORE_CW(variable)         /* NOP */
414 # define XPFPA_SWITCH_DOUBLE()              /* NOP */
415 # define XPFPA_SWITCH_SINGLE()              /* NOP */
416 # define XPFPA_SWITCH_DOUBLE_EXTENDED()     /* NOP */
417 # define XPFPA_RESTORE()                    /* NOP */
418 # define XPFPA_RETURN_DOUBLE(val)           return (val)
419 # define XPFPA_RETURN_SINGLE(val)           return (val)
420 # define XPFPA_RETURN_DOUBLE_EXTENDED(val)  return (val)
421 
422 #endif /* FPU CONTROL */
423 
424 #endif
425