php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
zend_float.h
Go to the documentation of this file.
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
25
26/*
27 Define functions for FP initialization and de-initialization.
28*/
29extern ZEND_API void zend_init_fpu(void);
30extern ZEND_API void zend_shutdown_fpu(void);
31extern ZEND_API void zend_ensure_fpu_mode(void);
32
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 operand 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
#define ZEND_API
ZEND_API void zend_shutdown_fpu(void)
Definition zend_float.c:39
ZEND_API void zend_init_fpu(void)
Definition zend_float.c:23
ZEND_API void zend_ensure_fpu_mode(void)
Definition zend_float.c:50
#define END_EXTERN_C()
#define BEGIN_EXTERN_C()