mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
- MFH: Changed floating point behaviour to consistently use double precision
on all platforms and with all compilers.
This commit is contained in:
parent
d5295fc9c8
commit
04c528609a
@ -115,6 +115,8 @@ AC_ZEND_BROKEN_SPRINTF
|
||||
AC_CHECK_FUNCS(finite isfinite isinf isnan)
|
||||
|
||||
ZEND_FP_EXCEPT
|
||||
|
||||
ZEND_CHECK_FLOAT_PRECISION
|
||||
|
||||
])
|
||||
|
||||
|
@ -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
|
||||
])
|
||||
|
10
Zend/tests/float_prec_001.phpt
Normal file
10
Zend/tests/float_prec_001.phpt
Normal file
@ -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"
|
355
Zend/zend_float.h
Normal file
355
Zend/zend_float.h
Normal file
@ -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
|
@ -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:
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user