]> granicus.if.org Git - php/commitdiff
- MFH: Changed floating point behaviour to consistently use double precision
authorChristian Seiler <cseiler@php.net>
Tue, 2 Dec 2008 16:19:10 +0000 (16:19 +0000)
committerChristian Seiler <cseiler@php.net>
Tue, 2 Dec 2008 16:19:10 +0000 (16:19 +0000)
  on all platforms and with all compilers.

Zend/Zend.m4
Zend/acinclude.m4
Zend/tests/float_prec_001.phpt [new file with mode: 0644]
Zend/zend_float.h [new file with mode: 0644]
Zend/zend_operators.c
Zend/zend_strtod.c

index 6de6b0cf13a28d0b099567f71abd3646a41ce800..904f1886816a4642f842ea2c1b89506966757b84 100644 (file)
@@ -115,6 +115,8 @@ AC_ZEND_BROKEN_SPRINTF
 AC_CHECK_FUNCS(finite isfinite isinf isnan)
 
 ZEND_FP_EXCEPT
+
+ZEND_CHECK_FLOAT_PRECISION
        
 ])
 
index 1c6ebd9baaac6931a5e4c917dc33b8677fc5c323..d507f1780ee7502c375cc5025baa6179b7a8543b 100644 (file)
@@ -105,3 +105,172 @@ int main(void)
 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
+])
diff --git a/Zend/tests/float_prec_001.phpt b/Zend/tests/float_prec_001.phpt
new file mode 100644 (file)
index 0000000..ffddab4
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+Double precision is used for floating point calculations
+--FILE--
+<?php
+var_dump (0.002877 == 2877.0 / 1000000.0);
+var_dump (substr (sprintf ("%.35f", 0.002877), 0, 10));
+?>
+--EXPECT--
+bool(true)
+string(10) "0.00287699"
diff --git a/Zend/zend_float.h b/Zend/zend_float.h
new file mode 100644 (file)
index 0000000..ee37328
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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
index b050a8d94d3a42188c829be6ab4abad22acfdd89..db60de52295e988b7af9b10f4e28945c96bf6575 100644 (file)
@@ -30,6 +30,7 @@
 #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))
 
@@ -775,6 +776,7 @@ ZEND_API void multi_convert_to_string_ex(int argc, ...)
 
 ZEND_API int add_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
 {
+       ZEND_FLOAT_DECLARE
        zval op1_copy, op2_copy;
        int converted = 0;
 
@@ -787,7 +789,9 @@ ZEND_API int add_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
                                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);
                                }
@@ -795,15 +799,21 @@ ZEND_API int add_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
                        }
 
                        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): {
@@ -837,6 +847,7 @@ ZEND_API int add_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
 
 ZEND_API int sub_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
 {
+       ZEND_FLOAT_DECLARE
        zval op1_copy, op2_copy;
        int converted = 0;
 
@@ -849,7 +860,9 @@ ZEND_API int sub_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
                                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);
                                }
@@ -857,15 +870,21 @@ ZEND_API int sub_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
 
                        }
                        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:
@@ -884,6 +903,7 @@ ZEND_API int sub_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
 
 ZEND_API int mul_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
 {
+       ZEND_FLOAT_DECLARE
        zval op1_copy, op2_copy;
        int converted = 0;
 
@@ -892,21 +912,29 @@ ZEND_API int mul_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
                        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:
@@ -924,6 +952,7 @@ ZEND_API int mul_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
 
 ZEND_API int div_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
 {
+       ZEND_FLOAT_DECLARE
        zval op1_copy, op2_copy;
        int converted = 0;
 
@@ -936,13 +965,17 @@ ZEND_API int div_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
                                        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;
 
@@ -952,7 +985,9 @@ ZEND_API int div_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
                                        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):
@@ -961,7 +996,9 @@ ZEND_API int div_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
                                        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):
@@ -970,7 +1007,9 @@ ZEND_API int div_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
                                        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:
index bc2ca97b19b413e882261f06cc4dad1558f0c6e6..4a3e976d259187a20e6ed9d884a585bf989aeefb 100644 (file)
@@ -93,6 +93,7 @@
 
 #include <zend_operators.h>
 #include <zend_strtod.h>
+#include <zend_float.h>
 
 #ifdef ZTS
 #include <TSRM.h>
@@ -2032,6 +2033,7 @@ ret1:
 
 ZEND_API double zend_strtod (CONST char *s00, char **se)
 {
+       ZEND_FLOAT_DECLARE
        int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign,
                e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
        CONST char *s, *s0, *s1;
@@ -2044,6 +2046,8 @@ ZEND_API double zend_strtod (CONST char *s00, char **se)
 
        CONST char decimal_point = '.';
 
+       ZEND_FLOAT_ENSURE();
+
        sign = nz0 = nz = 0;
        value(rv) = 0.;
 
@@ -2574,7 +2578,7 @@ ret:
        }
        _THREAD_PRIVATE_MUTEX_UNLOCK(pow5mult_mutex);
 
-       return result;
+       ZEND_FLOAT_RETURN(result);
 }
 
 ZEND_API double zend_hex_strtod(const char *str, char **endptr)