xref: /php-src/Zend/zend_float.h (revision 6335264c)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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 #ifndef ZEND_FLOAT_H
20 #define ZEND_FLOAT_H
21 
22 #include "zend_portability.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(void);
30 extern ZEND_API void zend_shutdown_fpu(void);
31 extern ZEND_API void zend_ensure_fpu_mode(void);
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, we do not define these
61     macros there. We ignore the compiler option -mfpmath=i387, because there is
62     no reason to use it on x86_64.
63 
64  General:
65   - It would be nice if one could detect whether SSE if used for math via some
66     funky compiler defines and if so, make the macros go to NOPs. Any ideas
67     on how to do that?
68 
69  MS Visual C:
70   - Since MSVC users typically don't use autoconf or CMake, we will detect
71     MSVC via compile time define.
72 */
73 
74 /* MSVC detection (MSVC people usually don't use autoconf) */
75 #if defined(_MSC_VER) && !defined(_WIN64)
76 #  define HAVE__CONTROLFP_S
77 #endif /* _MSC_VER */
78 
79 #if defined(HAVE__CONTROLFP_S) && !defined(__x86_64__)
80 
81 /* float.h defines _controlfp_s */
82 # include <float.h>
83 
84 # define XPFPA_HAVE_CW 1
85 # define XPFPA_CW_DATATYPE \
86             unsigned int
87 
88 # define XPFPA_STORE_CW(vptr) do { \
89             _controlfp_s((unsigned int *)(vptr), 0, 0); \
90         } while (0)
91 
92 # define XPFPA_RESTORE_CW(vptr) do { \
93             unsigned int _xpfpa_fpu_cw; \
94             _controlfp_s(&_xpfpa_fpu_cw, *((unsigned int *)(vptr)), _MCW_PC); \
95         } while (0)
96 
97 # define XPFPA_DECLARE \
98             unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
99 
100 # define XPFPA_SWITCH_DOUBLE() do { \
101             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
102             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
103             _controlfp_s(&_xpfpa_fpu_cw, _PC_53, _MCW_PC); \
104         } while (0)
105 # define XPFPA_SWITCH_SINGLE() do { \
106             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
107             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
108             _controlfp_s(&_xpfpa_fpu_cw, _PC_24, _MCW_PC); \
109         } while (0)
110 /* NOTE: This only sets internal precision. MSVC does NOT support double-
111    extended precision! */
112 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
113             _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
114             _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
115             _controlfp_s(&_xpfpa_fpu_cw, _PC_64, _MCW_PC); \
116         } while (0)
117 # define XPFPA_RESTORE() \
118             _controlfp_s(&_xpfpa_fpu_cw, _xpfpa_fpu_oldcw, _MCW_PC)
119 /* We do NOT use the volatile return trick since _controlfp_s is a function
120    call and thus FP registers are saved in memory anyway. However, we do use
121    a variable to ensure that the expression passed into val will be evaluated
122    *before* switching back contexts. */
123 # define XPFPA_RETURN_DOUBLE(val) \
124             do { \
125                 double _xpfpa_result = (val); \
126                 XPFPA_RESTORE(); \
127                 return _xpfpa_result; \
128             } while (0)
129 # define XPFPA_RETURN_SINGLE(val) \
130             do { \
131                 float _xpfpa_result = (val); \
132                 XPFPA_RESTORE(); \
133                 return _xpfpa_result; \
134             } while (0)
135 /* This won't work, but we add a macro for it anyway. */
136 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
137             do { \
138                 long double _xpfpa_result = (val); \
139                 XPFPA_RESTORE(); \
140                 return _xpfpa_result; \
141             } while (0)
142 
143 #elif defined(HAVE__CONTROLFP) && !defined(__x86_64__)
144 
145 /* float.h defines _controlfp */
146 # include <float.h>
147 
148 # define XPFPA_DECLARE \
149             unsigned int _xpfpa_fpu_oldcw;
150 
151 # define XPFPA_HAVE_CW 1
152 # define XPFPA_CW_DATATYPE \
153             unsigned int
154 
155 # define XPFPA_STORE_CW(vptr) do { \
156             *((unsigned int *)(vptr)) = _controlfp(0, 0); \
157         } while (0)
158 
159 # define XPFPA_RESTORE_CW(vptr) do { \
160             _controlfp(*((unsigned int *)(vptr)), _MCW_PC); \
161         } while (0)
162 
163 # define XPFPA_SWITCH_DOUBLE() do { \
164             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
165             _controlfp(_PC_53, _MCW_PC); \
166         } while (0)
167 # define XPFPA_SWITCH_SINGLE() do { \
168             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
169             _controlfp(_PC_24, _MCW_PC); \
170         } while (0)
171 /* NOTE: This will only work as expected on MinGW. */
172 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
173             _xpfpa_fpu_oldcw = _controlfp(0, 0); \
174             _controlfp(_PC_64, _MCW_PC); \
175         } while (0)
176 # define XPFPA_RESTORE() \
177             _controlfp(_xpfpa_fpu_oldcw, _MCW_PC)
178 /* We do NOT use the volatile return trick since _controlfp is a function
179    call and thus FP registers are saved in memory anyway. However, we do use
180    a variable to ensure that the expression passed into val will be evaluated
181    *before* switching back contexts. */
182 # define XPFPA_RETURN_DOUBLE(val) \
183             do { \
184                 double _xpfpa_result = (val); \
185                 XPFPA_RESTORE(); \
186                 return _xpfpa_result; \
187             } while (0)
188 # define XPFPA_RETURN_SINGLE(val) \
189             do { \
190                 float _xpfpa_result = (val); \
191                 XPFPA_RESTORE(); \
192                 return _xpfpa_result; \
193             } while (0)
194 /* This will only work on MinGW */
195 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
196             do { \
197                 long double _xpfpa_result = (val); \
198                 XPFPA_RESTORE(); \
199                 return _xpfpa_result; \
200             } while (0)
201 
202 #elif defined(HAVE__FPU_SETCW)  && !defined(__x86_64__) /* glibc systems */
203 
204 /* fpu_control.h defines _FPU_[GS]ETCW */
205 # include <fpu_control.h>
206 
207 # define XPFPA_DECLARE \
208             fpu_control_t _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
209 
210 # define XPFPA_HAVE_CW 1
211 # define XPFPA_CW_DATATYPE \
212             fpu_control_t
213 
214 # define XPFPA_STORE_CW(vptr) do { \
215             _FPU_GETCW((*((fpu_control_t *)(vptr)))); \
216         } while (0)
217 
218 # define XPFPA_RESTORE_CW(vptr) do { \
219             _FPU_SETCW((*((fpu_control_t *)(vptr)))); \
220         } while (0)
221 
222 # define XPFPA_SWITCH_DOUBLE() do { \
223             _FPU_GETCW(_xpfpa_fpu_oldcw); \
224             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE; \
225             _FPU_SETCW(_xpfpa_fpu_cw); \
226         } while (0)
227 # define XPFPA_SWITCH_SINGLE() do { \
228             _FPU_GETCW(_xpfpa_fpu_oldcw); \
229             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_DOUBLE) | _FPU_SINGLE; \
230             _FPU_SETCW(_xpfpa_fpu_cw); \
231         } while (0)
232 # define XPFPA_SWITCH_DOUBLE_EXTENDED()  do { \
233             _FPU_GETCW(_xpfpa_fpu_oldcw); \
234             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_SINGLE & ~_FPU_DOUBLE) | _FPU_EXTENDED; \
235             _FPU_SETCW(_xpfpa_fpu_cw); \
236         } while (0)
237 # define XPFPA_RESTORE() \
238             _FPU_SETCW(_xpfpa_fpu_oldcw)
239 /* We use a temporary volatile variable (in a new block) in order to ensure
240    that the optimizer does not mis-optimize the instructions. Also, a volatile
241    variable ensures truncation to correct precision. */
242 # define XPFPA_RETURN_DOUBLE(val) \
243             do { \
244                 volatile double _xpfpa_result = (val); \
245                 XPFPA_RESTORE(); \
246                 return _xpfpa_result; \
247             } while (0)
248 # define XPFPA_RETURN_SINGLE(val) \
249             do { \
250                 volatile float _xpfpa_result = (val); \
251                 XPFPA_RESTORE(); \
252                 return _xpfpa_result; \
253             } while (0)
254 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
255             do { \
256                 volatile long double _xpfpa_result = (val); \
257                 XPFPA_RESTORE(); \
258                 return _xpfpa_result; \
259             } while (0)
260 
261 #elif defined(HAVE_FPSETPREC)  && !defined(__x86_64__) /* FreeBSD */
262 
263 /* fpu_control.h defines _FPU_[GS]ETCW */
264 # include <machine/ieeefp.h>
265 
266 # define XPFPA_DECLARE \
267             fp_prec_t _xpfpa_fpu_oldprec;
268 
269 # define XPFPA_HAVE_CW 1
270 # define XPFPA_CW_DATATYPE \
271             fp_prec_t
272 
273 # define XPFPA_STORE_CW(vptr) do { \
274             *((fp_prec_t *)(vptr)) = fpgetprec(); \
275         } while (0)
276 
277 # define XPFPA_RESTORE_CW(vptr) do { \
278             fpsetprec(*((fp_prec_t *)(vptr))); \
279         } while (0)
280 
281 # define XPFPA_SWITCH_DOUBLE() do { \
282             _xpfpa_fpu_oldprec = fpgetprec(); \
283             fpsetprec(FP_PD); \
284         } while (0)
285 # define XPFPA_SWITCH_SINGLE() do { \
286             _xpfpa_fpu_oldprec = fpgetprec(); \
287             fpsetprec(FP_PS); \
288         } while (0)
289 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
290             _xpfpa_fpu_oldprec = fpgetprec(); \
291             fpsetprec(FP_PE); \
292         } while (0)
293 # define XPFPA_RESTORE() \
294             fpsetprec(_xpfpa_fpu_oldprec)
295 /* We use a temporary volatile variable (in a new block) in order to ensure
296    that the optimizer does not mis-optimize the instructions. Also, a volatile
297    variable ensures truncation to correct precision. */
298 # define XPFPA_RETURN_DOUBLE(val) \
299             do { \
300                 volatile double _xpfpa_result = (val); \
301                 XPFPA_RESTORE(); \
302                 return _xpfpa_result; \
303             } while (0)
304 # define XPFPA_RETURN_SINGLE(val) \
305             do { \
306                 volatile float _xpfpa_result = (val); \
307                 XPFPA_RESTORE(); \
308                 return _xpfpa_result; \
309             } while (0)
310 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
311             do { \
312                 volatile long double _xpfpa_result = (val); \
313                 XPFPA_RESTORE(); \
314                 return _xpfpa_result; \
315             } while (0)
316 
317 #elif defined(HAVE_FPU_INLINE_ASM_X86) && !defined(__x86_64__)
318 
319 /*
320   Custom x86 inline assembler implementation.
321 
322   This implementation does not use predefined wrappers of the OS / compiler
323   but rather uses x86/x87 inline assembler directly. Basic instructions:
324 
325   fnstcw - Store the FPU control word in a variable
326   fldcw  - Load the FPU control word from a variable
327 
328   Bits (only bits 8 and 9 are relevant, bits 0 to 7 are for other things):
329      0x0yy: Single precision
330      0x1yy: Reserved
331      0x2yy: Double precision
332      0x3yy: Double-extended precision
333 
334   We use an unsigned int for the datatype. glibc sources add __mode__ (__HI__)
335   attribute to it (HI stands for half-integer according to docs). It is unclear
336   what the does exactly and how portable it is.
337 
338   The assembly syntax works with GNU CC, Intel CC and Sun CC.
339 */
340 
341 # define XPFPA_DECLARE \
342             unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
343 
344 # define XPFPA_HAVE_CW 1
345 # define XPFPA_CW_DATATYPE \
346             unsigned int
347 
348 # define XPFPA_STORE_CW(vptr) do { \
349             __asm__ __volatile__ ("fnstcw %0" : "=m" (*((unsigned int *)(vptr)))); \
350         } while (0)
351 
352 # define XPFPA_RESTORE_CW(vptr) do { \
353             __asm__ __volatile__ ("fldcw %0" : : "m" (*((unsigned int *)(vptr)))); \
354         } while (0)
355 
356 # define XPFPA_SWITCH_DOUBLE() do { \
357             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
358             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x100) | 0x200; \
359             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
360         } while (0)
361 # define XPFPA_SWITCH_SINGLE() do { \
362             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
363             _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x300); \
364             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
365         } while (0)
366 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
367             __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
368             _xpfpa_fpu_cw = _xpfpa_fpu_oldcw | 0x300; \
369             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
370         } while (0)
371 # define XPFPA_RESTORE() \
372             __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_oldcw))
373 /* We use a temporary volatile variable (in a new block) in order to ensure
374    that the optimizer does not mis-optimize the instructions. Also, a volatile
375    variable ensures truncation to correct precision. */
376 # define XPFPA_RETURN_DOUBLE(val) \
377             do { \
378                 volatile double _xpfpa_result = (val); \
379                 XPFPA_RESTORE(); \
380                 return _xpfpa_result; \
381             } while (0)
382 # define XPFPA_RETURN_SINGLE(val) \
383             do { \
384                 volatile float _xpfpa_result = (val); \
385                 XPFPA_RESTORE(); \
386                 return _xpfpa_result; \
387             } while (0)
388 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
389             do { \
390                 volatile long double _xpfpa_result = (val); \
391                 XPFPA_RESTORE(); \
392                 return _xpfpa_result; \
393             } while (0)
394 
395 #else /* FPU CONTROL */
396 
397 /*
398   This is either not an x87 FPU or the inline assembly syntax was not
399   recognized. In any case, default to NOPs for the macros and hope the
400   generated code will behave as planned.
401 */
402 # define XPFPA_DECLARE                      /* NOP */
403 # define XPFPA_HAVE_CW                      0
404 # define XPFPA_CW_DATATYPE                  unsigned int
405 # define XPFPA_STORE_CW(variable)           /* NOP */
406 # define XPFPA_RESTORE_CW(variable)         /* NOP */
407 # define XPFPA_SWITCH_DOUBLE()              /* NOP */
408 # define XPFPA_SWITCH_SINGLE()              /* NOP */
409 # define XPFPA_SWITCH_DOUBLE_EXTENDED()     /* NOP */
410 # define XPFPA_RESTORE()                    /* NOP */
411 # define XPFPA_RETURN_DOUBLE(val)           return (val)
412 # define XPFPA_RETURN_SINGLE(val)           return (val)
413 # define XPFPA_RETURN_DOUBLE_EXTENDED(val)  return (val)
414 
415 #endif /* FPU CONTROL */
416 
417 #endif
418