]> granicus.if.org Git - php/commitdiff
Add _php_math_zvaltobase and _php_math_basetozval
authorStanislav Malyshev <stas@php.net>
Mon, 3 Sep 2001 16:26:31 +0000 (16:26 +0000)
committerStanislav Malyshev <stas@php.net>
Mon, 3 Sep 2001 16:26:31 +0000 (16:26 +0000)
This allows to use bindec, etc. functions to convert numbers longer
than fits in long, using floats instead.
Note: the functions like decbin still don't use the zval-ed functions,
so they work only on longs yet.

ext/standard/math.c

index dcf471e8553de8ee74fa9172a80935cf34bed770..18b403c76069eec6393f1907f81759b63b90bd91 100644 (file)
@@ -35,6 +35,7 @@
 char *_php_math_number_format(double, int, char , char);
 
 /* {{{ proto int abs(int number)
+
    Return the absolute value of the number */
 PHP_FUNCTION(abs) 
 {
@@ -698,6 +699,67 @@ _php_math_basetolong(zval *arg, int base) {
 /* }}} */
 /* {{{ _php_math_longtobase */
 
+/* {{{ _php_math_basetozval */
+
+/*
+ * Convert a string representation of a base(2-36) number to a zval.
+ */
+PHPAPI int
+_php_math_basetozval(zval *arg, int base, zval *ret) {
+       long mult = 1, num = 0, digit;
+       double fmult, fnum;
+       int i;
+       int f_mode = 0;
+       char c, *s;
+
+       if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
+               return FAILURE;
+       }
+
+       s = Z_STRVAL_P(arg);
+
+       for (i = Z_STRLEN_P(arg) - 1; i >= 0; i--) {
+               c = toupper(s[i]);
+               if (c >= '0' && c <= '9') {
+                       digit = (c - '0');
+               } else if (c >= 'A' && c <= 'Z') {
+                       digit = (c - 'A' + 10);
+               } else {
+                       continue;
+               }
+               if (digit >= base) {
+                       continue;
+               }
+               if(!f_mode && (!mult || digit > LONG_MAX/mult || num > LONG_MAX-mult*digit)) {
+                       f_mode = 1;
+                       if(!mult) {
+                               fmult = ULONG_MAX + 1;
+                       } else {
+                               fmult = (unsigned long)mult;
+                       }
+                       fnum = (unsigned long)num;
+               }
+               
+               if(f_mode) {
+                       fnum += fmult * digit;
+                       fmult *= base;
+               } else {
+                       num += mult * digit;
+                       mult *= base;
+               }
+       }
+
+       if(f_mode) {
+               ZVAL_DOUBLE(ret, fnum);
+       } else {
+               ZVAL_LONG(ret, num);
+       }
+       return SUCCESS;
+}
+
+/* }}} */
+/* {{{ _php_math_longtobase */
+
 /*
  * Convert a long to a string containing a base(2-36) representation of
  * the number.
@@ -736,6 +798,62 @@ _php_math_longtobase(zval *arg, int base)
        return ret;
 }      
 
+/* }}} */
+/* {{{ _php_math_zvaltobase */
+
+/*
+ * Convert a zval to a string containing a base(2-36) representation of
+ * the number.
+ */
+PHPAPI char *
+_php_math_zvaltobase(zval *arg, int base)
+{
+       static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+       char *result, *ptr, *ret;
+       int len, digit;
+       unsigned long value;
+       double fvalue;
+       int f_mode;
+
+       if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
+               return empty_string;
+       }
+
+       f_mode = (Z_TYPE_P(arg) == IS_DOUBLE);
+
+       if(f_mode) {
+               fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
+       } else {
+               value = Z_LVAL_P(arg);
+       }
+
+       /* allocates space for the longest possible result with the lowest base */
+       len = (sizeof(Z_DVAL_P(arg)) * 8) + 1;
+       result = emalloc((sizeof(Z_DVAL_P(arg)) * 8) + 1);
+
+       ptr = result + len - 1;
+       *ptr-- = '\0';
+
+       do {
+               if(f_mode) {
+                       double d = floor(fvalue/base);
+                       digit = (int)ceil(fvalue - d*base);
+                       *ptr = digits[digit];
+                       fvalue = d;
+               } else {
+                       digit = value % base;
+                       *ptr = digits[digit];
+                       value /= base;
+               }
+       }
+       while (ptr-- > result && (f_mode?(fabs(fvalue)>=1):value));
+       ptr++;
+       ret = estrdup(ptr);
+       efree(result);
+
+       return ret;
+}      
+
 /* }}} */
 /* {{{ proto int bindec(string binary_number)
    Returns the decimal equivalent of the binary number */
@@ -743,16 +861,15 @@ _php_math_longtobase(zval *arg, int base)
 PHP_FUNCTION(bindec)
 {
        zval **arg;
-       long ret;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
 
        convert_to_string_ex(arg);
-       ret = _php_math_basetolong(*arg, 2);
-
-       RETVAL_LONG(ret);
+       if(_php_math_basetozval(*arg, 2, return_value) != SUCCESS) {
+               RETURN_FALSE;
+       }
 }
 
 /* }}} */
@@ -762,7 +879,6 @@ PHP_FUNCTION(bindec)
 PHP_FUNCTION(hexdec)
 {
        zval **arg;
-       long ret;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
@@ -770,8 +886,9 @@ PHP_FUNCTION(hexdec)
 
        convert_to_string_ex(arg);
 
-       ret = _php_math_basetolong(*arg, 16);
-       RETVAL_LONG(ret);
+       if(_php_math_basetozval(*arg, 16, return_value) != SUCCESS) {
+               RETURN_FALSE;
+       }
 }
 
 /* }}} */
@@ -781,7 +898,6 @@ PHP_FUNCTION(hexdec)
 PHP_FUNCTION(octdec)
 {
        zval **arg;
-       long ret;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
@@ -789,8 +905,9 @@ PHP_FUNCTION(octdec)
 
        convert_to_string_ex(arg);
 
-       ret = _php_math_basetolong(*arg, 8);
-       RETVAL_LONG(ret);
+       if(_php_math_basetozval(*arg, 8, return_value) != SUCCESS) {
+               RETURN_FALSE;
+       }
 }
 
 /* }}} */
@@ -858,6 +975,7 @@ PHP_FUNCTION(dechex)
 
 /* }}} */
 /* {{{ proto string base_convert(string number, int frombase, int tobase)
+
    Converts a number in a string from any base <= 36 to any base <= 36.
 */
 
@@ -880,9 +998,11 @@ PHP_FUNCTION(base_convert)
                php_error(E_WARNING, "base_convert: invalid `to base' (%d)", Z_LVAL_PP(tobase));
                RETURN_FALSE;
        }
-       Z_TYPE(temp) = IS_LONG;
-       Z_LVAL(temp) = _php_math_basetolong(*number, Z_LVAL_PP(frombase));
-       result = _php_math_longtobase(&temp, Z_LVAL_PP(tobase));
+
+       if(_php_math_basetozval(*number, Z_LVAL_PP(frombase), &temp) != SUCCESS) {
+               RETURN_FALSE;
+       }
+       result = _php_math_zvaltobase(&temp, Z_LVAL_PP(tobase));
        RETVAL_STRING(result, 0);
 }