summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArd Biesheuvel <abies@php.net>2004-03-10 15:57:49 +0000
committerArd Biesheuvel <abies@php.net>2004-03-10 15:57:49 +0000
commite6281ab38bb6cd87cef572a12811b68f1f6785cc (patch)
tree562bce1ca792524888bb52d472d91d64a87b3840
parentb63def4dfc1b7bb7ed8a528f1632587469bb3262 (diff)
downloadphp-git-e6281ab38bb6cd87cef572a12811b68f1f6785cc.tar.gz
Don't use fp arithmetic for int results
# FP arithmetic is not accurate enough on 64-bit archs # This patch relies on ZEND_SIGNED_MULTIPLY_LONG(), # so that should be fixed as well.
-rw-r--r--ext/standard/math.c41
1 files changed, 29 insertions, 12 deletions
diff --git a/ext/standard/math.c b/ext/standard/math.c
index 6d58ba54df..262ec23e1d 100644
--- a/ext/standard/math.c
+++ b/ext/standard/math.c
@@ -23,6 +23,7 @@
#include "php.h"
#include "php_math.h"
+#include "zend_multiply.h"
#include <math.h>
#include <float.h>
@@ -449,22 +450,38 @@ PHP_FUNCTION(pow)
convert_scalar_to_number(zexp TSRMLS_CC);
/* if both base and exponent were longs, we'll try to get a long out */
- wantlong = Z_TYPE_P(zbase) == IS_LONG
- && Z_TYPE_P(zexp ) == IS_LONG && Z_LVAL_P(zexp) >= 0;
+ if (Z_TYPE_P(zbase) == IS_LONG && Z_TYPE_P(zexp) == IS_LONG && Z_LVAL_P(zexp) >= 0) {
+ long l1 = 1, l2 = Z_LVAL_P(zbase), i = Z_LVAL_P(zexp);
+
+ if (i == 0) {
+ RETURN_LONG(1L);
+ } else if (l2 == 0) {
+ RETURN_LONG(0);
+ }
+
+ /* calculate pow(long,long) in O(log exp) operations, bail if overflow */
+ while (i >= 1) {
+ int overflow;
+ double dval;
+ if (i % 2) {
+ --i;
+ ZEND_SIGNED_MULTIPLY_LONG(l1,l2,l1,dval,overflow);
+ if (overflow) RETURN_DOUBLE(dval * pow(l2,i));
+ } else {
+ i /= 2;
+ ZEND_SIGNED_MULTIPLY_LONG(l2,l2,l2,dval,overflow);
+ if (overflow) RETURN_DOUBLE((double)l1 * pow(dval,i));
+ }
+ if (i == 0) {
+ RETURN_LONG(l1);
+ }
+ }
+ }
convert_to_double(zbase);
convert_to_double(zexp);
- /* go ahead and calculate things. */
- dval = pow(Z_DVAL_P(zbase),Z_DVAL_P(zexp));
-
- /* if we wanted a long, and dval < LONG_MAX, it must be a long. */
- if (wantlong && zend_finite(dval) && dval <= (double)LONG_MAX) {
- RETURN_LONG((long)dval);
- }
-
- /* otherwise just return the double. */
- RETURN_DOUBLE(dval);
+ RETURN_DOUBLE( pow(Z_DVAL_P(zbase),Z_DVAL_P(zexp)) );
}
/* }}} */