]> granicus.if.org Git - php/commitdiff
Don't use fp arithmetic for int results
authorArd Biesheuvel <abies@php.net>
Wed, 10 Mar 2004 15:57:49 +0000 (15:57 +0000)
committerArd Biesheuvel <abies@php.net>
Wed, 10 Mar 2004 15:57:49 +0000 (15:57 +0000)
# 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.

ext/standard/math.c

index 6d58ba54df7eefea8b8a24997bd372169a2632ae..262ec23e1d1ac2aceec9e73791997fbc5f41d7b4 100644 (file)
@@ -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)) );
 }
 /* }}} */