AC_DEFUN([AM_SET_LIBTOOL_VARIABLE],[
LIBTOOL='$(SHELL) $(top_builddir)/libtool $1'
])
+
+dnl x87 floating point internal precision control checks
+dnl See: http://wiki.php.net/rfc/rounding
+AC_DEFUN([ZEND_CHECK_FLOAT_PRECISION],[
+ AC_MSG_CHECKING([for usable _FPU_SETCW])
+ AC_LINK_IFELSE([[
+ #include <stdio.h>
+ #include <string.h>
+ #include <fpu_control.h>
+
+ double div (double a, double b) {
+ fpu_control_t fpu_oldcw, fpu_cw;
+ volatile double result;
+
+ _FPU_GETCW(fpu_oldcw);
+ fpu_cw = (fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE;
+ _FPU_SETCW(fpu_cw);
+ result = a / b;
+ _FPU_SETCW(fpu_oldcw);
+ return result;
+ }
+
+ int main (int argc, char **argv) {
+ double d = div (2877.0, 1000000.0);
+ char buf[255];
+ sprintf(buf, "%.30f", d);
+ /* see if the result is actually in double precision */
+ return strncmp(buf, "0.00287699", 10) == 0 ? 0 : 1;
+ }
+ ]], [ac_cfp_have__fpu_setcw=yes], [ac_cfp_have__fpu_setcw=no])
+ if test "$ac_cfp_have__fpu_setcw" = "yes" ; then
+ AC_DEFINE(HAVE__FPU_SETCW, 1, [whether _FPU_SETCW is present and usable])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+
+ AC_MSG_CHECKING([for usable fpsetprec])
+ AC_LINK_IFELSE([[
+ #include <stdio.h>
+ #include <string.h>
+ #include <machine/ieeefp.h>
+
+ double div (double a, double b) {
+ fp_prec_t fpu_oldprec;
+ volatile double result;
+
+ fpu_oldprec = fpgetprec();
+ fpsetprec(FP_PD);
+ result = a / b;
+ fpsetprec(fpu_oldprec);
+ return result;
+ }
+
+ int main (int argc, char **argv) {
+ double d = div (2877.0, 1000000.0);
+ char buf[255];
+ sprintf(buf, "%.30f", d);
+ /* see if the result is actually in double precision */
+ return strncmp(buf, "0.00287699", 10) == 0 ? 0 : 1;
+ }
+ ]], [ac_cfp_have_fpsetprec=yes], [ac_cfp_have_fpsetprec=no])
+ if test "$ac_cfp_have_fpsetprec" = "yes" ; then
+ AC_DEFINE(HAVE_FPSETPREC, 1, [whether fpsetprec is present and usable])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+
+ AC_MSG_CHECKING([for usable _controlfp])
+ AC_LINK_IFELSE([[
+ #include <stdio.h>
+ #include <string.h>
+ #include <float.h>
+
+ double div (double a, double b) {
+ unsigned int fpu_oldcw;
+ volatile double result;
+
+ fpu_oldcw = _controlfp(0, 0);
+ _controlfp(_PC_53, _MCW_PC);
+ result = a / b;
+ _controlfp(fpu_oldcw, _MCW_PC);
+ return result;
+ }
+
+ int main (int argc, char **argv) {
+ double d = div (2877.0, 1000000.0);
+ char buf[255];
+ sprintf(buf, "%.30f", d);
+ /* see if the result is actually in double precision */
+ return strncmp(buf, "0.00287699", 10) == 0 ? 0 : 1;
+ }
+ ]], [ac_cfp_have__controlfp=yes], [ac_cfp_have__controlfp=no])
+ if test "$ac_cfp_have__controlfp" = "yes" ; then
+ AC_DEFINE(HAVE__CONTROLFP, 1, [whether _controlfp is present usable])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+
+ AC_MSG_CHECKING([for usable _controlfp_s])
+ AC_LINK_IFELSE([[
+ #include <stdio.h>
+ #include <string.h>
+ #include <float.h>
+
+ double div (double a, double b) {
+ unsigned int fpu_oldcw, fpu_cw;
+ volatile double result;
+
+ _controlfp_s(&fpu_cw, 0, 0);
+ fpu_oldcw = fpu_cw;
+ _controlfp_s(&fpu_cw, _PC_53, _MCW_PC);
+ result = a / b;
+ _controlfp_s(&fpu_cw, fpu_oldcw, _MCW_PC);
+ return result;
+ }
+
+ int main (int argc, char **argv) {
+ double d = div (2877.0, 1000000.0);
+ char buf[255];
+ sprintf(buf, "%.30f", d);
+ /* see if the result is actually in double precision */
+ return strncmp(buf, "0.00287699", 10) == 0 ? 0 : 1;
+ }
+ ]], [ac_cfp_have__controlfp_s=yes], [ac_cfp_have__controlfp_s=no])
+ if test "$ac_cfp_have__controlfp_s" = "yes" ; then
+ AC_DEFINE(HAVE__CONTROLFP_S, 1, [whether _controlfp_s is present and usable])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+
+ AC_MSG_CHECKING([whether FPU control word can be manipulated by inline assembler])
+ AC_LINK_IFELSE([[
+ #include <stdio.h>
+ #include <string.h>
+
+ double div (double a, double b) {
+ unsigned int oldcw, cw;
+ volatile double result;
+
+ __asm__ __volatile__ ("fnstcw %0" : "=m" (*&oldcw));
+ cw = (oldcw & ~0x0 & ~0x300) | 0x200;
+ __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw));
+
+ result = a / b;
+
+ __asm__ __volatile__ ("fldcw %0" : : "m" (*&oldcw));
+
+ return result;
+ }
+
+ int main (int argc, char **argv) {
+ double d = div (2877.0, 1000000.0);
+ char buf[255];
+ sprintf(buf, "%.30f", d);
+ /* see if the result is actually in double precision */
+ return strncmp(buf, "0.00287699", 10) == 0 ? 0 : 1;
+ }
+ ]], [ac_cfp_have_fpu_inline_asm_x86=yes], [ac_cfp_have_fpu_inline_asm_x86=no])
+ if test "$ac_cfp_have_fpu_inline_asm_x86" = "yes" ; then
+ AC_DEFINE(HAVE_FPU_INLINE_ASM_X86, 1, [whether FPU control word can be manipulated by inline assembler])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+])
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2007 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Christian Seiler <chris_se@gmx.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef ZEND_FLOAT_H
+#define ZEND_FLOAT_H
+
+#define ZEND_FLOAT_DECLARE XPFPA_DECLARE
+#define ZEND_FLOAT_ENSURE() XPFPA_SWITCH_DOUBLE()
+#define ZEND_FLOAT_RESTORE() XPFPA_RESTORE()
+#define ZEND_FLOAT_RETURN(val) XPFPA_RETURN_DOUBLE(val)
+
+/* Copy of the contents of xpfpa.h (which is under public domain)
+ See http://wiki.php.net/rfc/rounding for details.
+
+ Cross Platform Floating Point Arithmetics
+
+ This header file defines several platform-dependent macros that ensure
+ equal and deterministic floating point behaviour across several platforms,
+ compilers and architectures.
+
+ The current macros are currently only used on x86 and x86_64 architectures,
+ on every other architecture, these macros expand to NOPs. This assumes that
+ other architectures do not have an internal precision and the operhand types
+ define the computational precision of floating point operations. This
+ assumption may be false, in that case, the author is interested in further
+ details on the other platform.
+
+ For further details, please visit:
+ http://www.christian-seiler.de/projekte/fpmath/
+
+ Version: 20081026 */
+
+/*
+ Implementation notes:
+
+ x86_64:
+ - Since all x86_64 compilers use SSE by default, it is probably unecessary
+ to use these macros there. We define them anyway since we are too lazy
+ to differentiate the architecture. Also, the compiler option -mfpmath=i387
+ justifies this decision.
+
+ General:
+ - It would be nice if one could detect whether SSE if used for math via some
+ funky compiler defines and if so, make the macros go to NOPs. Any ideas
+ on how to do that?
+
+ MS Visual C:
+ - Since MSVC users tipically don't use autoconf or CMake, we will detect
+ MSVC via compile time define.
+*/
+
+/* MSVC detection (MSVC people usually don't use autoconf) */
+#ifdef _MSC_VER
+# if _MSC_VER >= 1500
+ /* Visual C++ 2008 or higher, supports _controlfp_s */
+# define HAVE__CONTROLFP_S
+# else
+ /* Visual C++ (up to 2005), supports _controlfp */
+# define HAVE__CONTROLFP
+# endif /* MSC_VER >= 1500 */
+ /* Tell MSVC optimizer that we access FP environment */
+# pragma fenv_access (on)
+#endif /* _MSC_VER */
+
+#ifdef HAVE__CONTROLFP_S
+
+/* float.h defines _controlfp_s */
+# include <float.h>
+
+# define XPFPA_DECLARE \
+ unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
+
+# define XPFPA_SWITCH_DOUBLE() do { \
+ _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
+ _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
+ _controlfp_s(&_xpfpa_fpu_cw, _PC_53, _MCW_PC); \
+ } while (0)
+# define XPFPA_SWITCH_SINGLE() do { \
+ _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
+ _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
+ _controlfp_s(&_xpfpa_fpu_cw, _PC_24, _MCW_PC); \
+ } while (0)
+/* NOTE: This only sets internal precision. MSVC does NOT support double-
+ extended precision! */
+# define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
+ _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \
+ _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \
+ _controlfp_s(&_xpfpa_fpu_cw, _PC_64, _MCW_PC); \
+ } while (0)
+# define XPFPA_RESTORE() \
+ _controlfp_s(&_xpfpa_fpu_cw, _xpfpa_fpu_oldcw, _MCW_PC)
+/* We do NOT use the volatile return trick since _controlfp_s is a function
+ call and thus FP registers are saved in memory anyway. However, we do use
+ a variable to ensure that the expression passed into val will be evaluated
+ *before* switching back contexts. */
+# define XPFPA_RETURN_DOUBLE(val) \
+ do { \
+ double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+# define XPFPA_RETURN_SINGLE(val) \
+ do { \
+ float _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+/* This won't work, but we add a macro for it anyway. */
+# define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
+ do { \
+ long double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+
+#elif defined(HAVE__CONTROLFP)
+
+/* float.h defines _controlfp */
+# include <float.h>
+
+# define XPFPA_DECLARE \
+ unsigned int _xpfpa_fpu_oldcw;
+
+# define XPFPA_SWITCH_DOUBLE() do { \
+ _xpfpa_fpu_oldcw = _controlfp(0, 0); \
+ _controlfp(_PC_53, _MCW_PC); \
+ } while (0)
+# define XPFPA_SWITCH_SINGLE() do { \
+ _xpfpa_fpu_oldcw = _controlfp(0, 0); \
+ _controlfp(_PC_24, _MCW_PC); \
+ } while (0)
+/* NOTE: This will only work as expected on MinGW. */
+# define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
+ _xpfpa_fpu_oldcw = _controlfp(0, 0); \
+ _controlfp(_PC_64, _MCW_PC); \
+ } while (0)
+# define XPFPA_RESTORE() \
+ _controlfp(_xpfpa_fpu_oldcw, _MCW_PC)
+/* We do NOT use the volatile return trick since _controlfp is a function
+ call and thus FP registers are saved in memory anyway. However, we do use
+ a variable to ensure that the expression passed into val will be evaluated
+ *before* switching back contexts. */
+# define XPFPA_RETURN_DOUBLE(val) \
+ do { \
+ double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+# define XPFPA_RETURN_SINGLE(val) \
+ do { \
+ float _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+/* This will only work on MinGW */
+# define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
+ do { \
+ long double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+
+#elif defined(HAVE__FPU_SETCW) /* glibc systems */
+
+/* fpu_control.h defines _FPU_[GS]ETCW */
+# include <fpu_control.h>
+
+# define XPFPA_DECLARE \
+ fpu_control_t _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
+
+# define XPFPA_SWITCH_DOUBLE() do { \
+ _FPU_GETCW(_xpfpa_fpu_oldcw); \
+ _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE; \
+ _FPU_SETCW(_xpfpa_fpu_cw); \
+ } while (0)
+# define XPFPA_SWITCH_SINGLE() do { \
+ _FPU_GETCW(_xpfpa_fpu_oldcw); \
+ _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_DOUBLE) | _FPU_SINGLE; \
+ _FPU_SETCW(_xpfpa_fpu_cw); \
+ } while (0)
+# define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
+ _FPU_GETCW(_xpfpa_fpu_oldcw); \
+ _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_SINGLE & ~_FPU_DOUBLE) | _FPU_EXTENDED; \
+ _FPU_SETCW(_xpfpa_fpu_cw); \
+ } while (0)
+# define XPFPA_RESTORE() \
+ _FPU_SETCW(_xpfpa_fpu_oldcw)
+/* We use a temporary volatile variable (in a new block) in order to ensure
+ that the optimizer does not mis-optimize the instructions. Also, a volatile
+ variable ensures truncation to correct precision. */
+# define XPFPA_RETURN_DOUBLE(val) \
+ do { \
+ volatile double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+# define XPFPA_RETURN_SINGLE(val) \
+ do { \
+ volatile float _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+# define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
+ do { \
+ volatile long double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+
+#elif defined(HAVE_FPSETPREC) /* FreeBSD */
+
+/* fpu_control.h defines _FPU_[GS]ETCW */
+# include <machine/ieeefp.h>
+
+# define XPFPA_DECLARE \
+ fp_prec_t _xpfpa_fpu_oldprec;
+
+# define XPFPA_SWITCH_DOUBLE() do { \
+ _xpfpa_fpu_oldprec = fpgetprec(); \
+ fpsetprec(FP_PD); \
+ } while (0)
+# define XPFPA_SWITCH_SINGLE() do { \
+ _xpfpa_fpu_oldprec = fpgetprec(); \
+ fpsetprec(FP_PS); \
+ } while (0)
+# define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
+ _xpfpa_fpu_oldprec = fpgetprec(); \
+ fpsetprec(FP_PE); \
+ } while (0)
+# define XPFPA_RESTORE() \
+ fpsetprec(_xpfpa_fpu_oldprec)
+/* We use a temporary volatile variable (in a new block) in order to ensure
+ that the optimizer does not mis-optimize the instructions. Also, a volatile
+ variable ensures truncation to correct precision. */
+# define XPFPA_RETURN_DOUBLE(val) \
+ do { \
+ volatile double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+# define XPFPA_RETURN_SINGLE(val) \
+ do { \
+ volatile float _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+# define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
+ do { \
+ volatile long double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+
+#elif defined(HAVE_FPU_INLINE_ASM_X86)
+
+/*
+ Custom x86 inline assembler implementation.
+
+ This implementation does not use predefined wrappers of the OS / compiler
+ but rather uses x86/x87 inline assembler directly. Basic instructions:
+
+ fnstcw - Store the FPU control word in a variable
+ fldcw - Load the FPU control word from a variable
+
+ Bits (only bits 8 and 9 are relevant, bits 0 to 7 are for other things):
+ 0x0yy: Single precision
+ 0x1yy: Reserved
+ 0x2yy: Double precision
+ 0x3yy: Double-extended precision
+
+ We use an unsigned int for the datatype. glibc sources add __mode__ (__HI__)
+ attribute to it (HI stands for half-integer according to docs). It is unclear
+ what the does exactly and how portable it is.
+
+ The assembly syntax works with GNU CC, Intel CC and Sun CC.
+*/
+
+# define XPFPA_DECLARE \
+ unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw;
+
+# define XPFPA_SWITCH_DOUBLE() do { \
+ __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
+ _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x100) | 0x200; \
+ __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
+ } while (0)
+# define XPFPA_SWITCH_SINGLE() do { \
+ __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
+ _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x300); \
+ __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
+ } while (0)
+# define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \
+ __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \
+ _xpfpa_fpu_cw = _xpfpa_fpu_oldcw | 0x300; \
+ __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \
+ } while (0)
+# define XPFPA_RESTORE() \
+ __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_oldcw))
+/* We use a temporary volatile variable (in a new block) in order to ensure
+ that the optimizer does not mis-optimize the instructions. Also, a volatile
+ variable ensures truncation to correct precision. */
+# define XPFPA_RETURN_DOUBLE(val) \
+ do { \
+ volatile double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+# define XPFPA_RETURN_SINGLE(val) \
+ do { \
+ volatile float _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+# define XPFPA_RETURN_DOUBLE_EXTENDED(val) \
+ do { \
+ volatile long double _xpfpa_result = (val); \
+ XPFPA_RESTORE(); \
+ return _xpfpa_result; \
+ } while (0)
+
+#else /* FPU CONTROL */
+
+/*
+ This is either not an x87 FPU or the inline assembly syntax was not
+ recognized. In any case, default to NOPs for the macros and hope the
+ generated code will behave as planned.
+*/
+# define XPFPA_DECLARE /* NOP */
+# define XPFPA_SWITCH_DOUBLE() /* NOP */
+# define XPFPA_SWITCH_SINGLE() /* NOP */
+# define XPFPA_SWITCH_DOUBLE_EXTENDED() /* NOP */
+# define XPFPA_RESTORE() /* NOP */
+# define XPFPA_RETURN_DOUBLE(val) return (val)
+# define XPFPA_RETURN_SINGLE(val) return (val)
+# define XPFPA_RETURN_DOUBLE_EXTENDED(val) return (val)
+
+#endif /* FPU CONTROL */
+
+#endif
#include "zend_multiply.h"
#include "zend_strtod.h"
#include "zend_exceptions.h"
+#include "zend_float.h"
#define LONG_SIGN_MASK (1L << (8*sizeof(long)-1))
ZEND_API int add_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
{
+ ZEND_FLOAT_DECLARE
zval op1_copy, op2_copy;
int converted = 0;
if ((Z_LVAL_P(op1) & LONG_SIGN_MASK) == (Z_LVAL_P(op2) & LONG_SIGN_MASK)
&& (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (lval & LONG_SIGN_MASK)) {
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
} else {
ZVAL_LONG(result, lval);
}
}
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_LONG):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2)));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_ARRAY, IS_ARRAY): {
ZEND_API int sub_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
{
+ ZEND_FLOAT_DECLARE
zval op1_copy, op2_copy;
int converted = 0;
if ((Z_LVAL_P(op1) & LONG_SIGN_MASK) != (Z_LVAL_P(op2) & LONG_SIGN_MASK)
&& (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (lval & LONG_SIGN_MASK)) {
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
} else {
ZVAL_LONG(result, lval);
}
}
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) - Z_DVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_LONG):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, Z_DVAL_P(op1) - ((double)Z_LVAL_P(op2)));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, Z_DVAL_P(op1) - Z_DVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
default:
ZEND_API int mul_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
{
+ ZEND_FLOAT_DECLARE
zval op1_copy, op2_copy;
int converted = 0;
case TYPE_PAIR(IS_LONG, IS_LONG): {
long overflow;
+ ZEND_FLOAT_ENSURE();
ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(op1),Z_LVAL_P(op2), Z_LVAL_P(result),Z_DVAL_P(result),overflow);
+ ZEND_FLOAT_RESTORE();
Z_TYPE_P(result) = overflow ? IS_DOUBLE : IS_LONG;
return SUCCESS;
}
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) * Z_DVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_LONG):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, Z_DVAL_P(op1) * ((double)Z_LVAL_P(op2)));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, Z_DVAL_P(op1) * Z_DVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
default:
ZEND_API int div_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
{
+ ZEND_FLOAT_DECLARE
zval op1_copy, op2_copy;
int converted = 0;
return FAILURE; /* division by zero */
} else if (Z_LVAL_P(op2) == -1 && Z_LVAL_P(op1) == LONG_MIN) {
/* Prevent overflow error/crash */
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, (double) LONG_MIN / -1);
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
}
if (Z_LVAL_P(op1) % Z_LVAL_P(op2) == 0) { /* integer */
ZVAL_LONG(result, Z_LVAL_P(op1) / Z_LVAL_P(op2));
} else {
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, ((double) Z_LVAL_P(op1)) / Z_LVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
}
return SUCCESS;
ZVAL_BOOL(result, 0);
return FAILURE; /* division by zero */
}
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, Z_DVAL_P(op1) / (double)Z_LVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
ZVAL_BOOL(result, 0);
return FAILURE; /* division by zero */
}
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, (double)Z_LVAL_P(op1) / Z_DVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
ZVAL_BOOL(result, 0);
return FAILURE; /* division by zero */
}
+ ZEND_FLOAT_ENSURE();
ZVAL_DOUBLE(result, Z_DVAL_P(op1) / Z_DVAL_P(op2));
+ ZEND_FLOAT_RESTORE();
return SUCCESS;
default: