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