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