Back to index

php5  5.3.10
math.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Jim Winstead <jimw@php.net>                                 |
00016    |          Stig Sæther Bakken <ssb@php.net>                            |
00017    |          Zeev Suraski <zeev@zend.com>                                |
00018    | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net>               |
00019    +----------------------------------------------------------------------+
00020 */
00021 
00022 /* $Id: math.c 321634 2012-01-01 13:15:04Z felipe $ */
00023 
00024 #include "php.h"
00025 #include "php_math.h"
00026 #include "zend_multiply.h"
00027 
00028 #include <math.h>
00029 #include <float.h>
00030 #include <stdlib.h>
00031 
00032 #include "basic_functions.h"
00033 
00034 /* {{{ php_intlog10abs
00035    Returns floor(log10(fabs(val))), uses fast binary search */
00036 static inline int php_intlog10abs(double value) {
00037        int result;
00038        value = fabs(value);
00039 
00040        if (value < 1e-8 || value > 1e23) {
00041               result = (int)floor(log10(value));
00042        } else {
00043               static const double values[] = {
00044                      1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
00045                      1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
00046                      1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
00047                      1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
00048               /* Do a binary search with 5 steps */
00049               result = 16;
00050               if (value < values[result]) {
00051                      result -= 8;
00052               } else {
00053                      result += 8;
00054               }
00055               if (value < values[result]) {
00056                      result -= 4;
00057               } else {
00058                      result += 4;
00059               }
00060               if (value < values[result]) {
00061                      result -= 2;
00062               } else {
00063                      result += 2;
00064               }
00065               if (value < values[result]) {
00066                      result -= 1;
00067               } else {
00068                      result += 1;
00069               }
00070               if (value < values[result]) {
00071                      result -= 1;
00072               }
00073               result -= 8;
00074        }
00075        return result;
00076 }
00077 /* }}} */
00078 
00079 /* {{{ php_intpow10
00080        Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
00081 static inline double php_intpow10(int power) {
00082        static const double powers[] = {
00083               1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
00084               1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
00085               1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
00086 
00087        /* Not in lookup table */
00088        if (power < 0 || power > 22) {
00089               return pow(10.0, (double)power);
00090        }
00091        return powers[power];
00092 }
00093 /* }}} */
00094 
00095 /* {{{ php_math_is_finite */
00096 static inline int php_math_is_finite(double value) {
00097 #if defined(PHP_WIN32)
00098        return _finite(value);
00099 #elif defined(isfinite)
00100        return isfinite(value);
00101 #else
00102        return value == value && (value == 0. || value * 2. != value);
00103 #endif
00104 }
00105 /* }}} */
00106 
00107 /* {{{ php_round_helper
00108        Actually performs the rounding of a value to integer in a certain mode */
00109 static inline double php_round_helper(double value, int mode) {
00110        double tmp_value;
00111 
00112        if (value >= 0.0) {
00113               tmp_value = floor(value + 0.5);
00114               if ((mode == PHP_ROUND_HALF_DOWN && value == (-0.5 + tmp_value)) ||
00115                      (mode == PHP_ROUND_HALF_EVEN && value == (0.5 + 2 * floor(tmp_value/2.0))) ||
00116                      (mode == PHP_ROUND_HALF_ODD  && value == (0.5 + 2 * floor(tmp_value/2.0) - 1.0)))
00117               {
00118                      tmp_value = tmp_value - 1.0;
00119               }
00120        } else {
00121               tmp_value = ceil(value - 0.5);
00122               if ((mode == PHP_ROUND_HALF_DOWN && value == (0.5 + tmp_value)) ||
00123                      (mode == PHP_ROUND_HALF_EVEN && value == (-0.5 + 2 * ceil(tmp_value/2.0))) ||
00124                      (mode == PHP_ROUND_HALF_ODD  && value == (-0.5 + 2 * ceil(tmp_value/2.0) + 1.0)))
00125               {
00126                      tmp_value = tmp_value + 1.0;
00127               }
00128        }
00129 
00130        return tmp_value;
00131 }
00132 /* }}} */
00133 
00134 /* {{{ _php_math_round */
00135 /*
00136  * Rounds a number to a certain number of decimal places in a certain rounding
00137  * mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding
00138  */
00139 PHPAPI double _php_math_round(double value, int places, int mode) {
00140        double f1, f2;
00141        double tmp_value;
00142        int precision_places;
00143 
00144        if (!php_math_is_finite(value)) {
00145               return value;
00146        }
00147        
00148        precision_places = 14 - php_intlog10abs(value);
00149 
00150        f1 = php_intpow10(abs(places));
00151 
00152        /* If the decimal precision guaranteed by FP arithmetic is higher than
00153           the requested places BUT is small enough to make sure a non-zero value
00154           is returned, pre-round the result to the precision */
00155        if (precision_places > places && precision_places - places < 15) {
00156               f2 = php_intpow10(abs(precision_places));
00157               if (precision_places >= 0) {
00158                      tmp_value = value * f2;
00159               } else {
00160                      tmp_value = value / f2;
00161               }
00162               /* preround the result (tmp_value will always be something * 1e14,
00163                  thus never larger than 1e15 here) */
00164               tmp_value = php_round_helper(tmp_value, mode);
00165               /* now correctly move the decimal point */
00166               f2 = php_intpow10(abs(places - precision_places));
00167               /* because places < precision_places */
00168               tmp_value = tmp_value / f2;
00169        } else {
00170               /* adjust the value */
00171               if (places >= 0) {
00172                      tmp_value = value * f1;
00173               } else {
00174                      tmp_value = value / f1;
00175               }
00176               /* This value is beyond our precision, so rounding it is pointless */
00177               if (fabs(tmp_value) >= 1e15) {
00178                      return value;
00179               }
00180        }
00181 
00182        /* round the temp value */
00183        tmp_value = php_round_helper(tmp_value, mode);
00184        
00185        /* see if it makes sense to use simple division to round the value */
00186        if (abs(places) < 23) {
00187               if (places > 0) {
00188                      tmp_value = tmp_value / f1;
00189               } else {
00190                      tmp_value = tmp_value * f1;
00191               }
00192        } else {
00193               /* Simple division can't be used since that will cause wrong results.
00194                  Instead, the number is converted to a string and back again using
00195                  strtod(). strtod() will return the nearest possible FP value for
00196                  that string. */
00197 
00198               /* 40 Bytes should be more than enough for this format string. The
00199                  float won't be larger than 1e15 anyway. But just in case, use
00200                  snprintf() and make sure the buffer is zero-terminated */
00201               char buf[40];
00202               snprintf(buf, 39, "%15fe%d", tmp_value, -places);
00203               buf[39] = '\0';
00204               tmp_value = zend_strtod(buf, NULL);
00205               /* couldn't convert to string and back */
00206               if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
00207                      tmp_value = value;
00208               }
00209        }
00210 
00211        return tmp_value;
00212 }
00213 /* }}} */
00214 
00215 /* {{{ php_asinh
00216 */
00217 static double php_asinh(double z)
00218 {
00219 #ifdef HAVE_ASINH
00220        return(asinh(z));
00221 #else
00222        return(log(z + sqrt(1 + pow(z, 2))) / log(M_E));
00223 #endif
00224 }
00225 /* }}} */
00226 
00227 /* {{{ php_acosh
00228 */
00229 static double php_acosh(double x)
00230 {
00231 #ifdef HAVE_ACOSH
00232        return(acosh(x));
00233 #else
00234        return(log(x + sqrt(x * x - 1)));
00235 #endif
00236 }
00237 /* }}} */
00238 
00239 /* {{{ php_atanh
00240 */
00241 static double php_atanh(double z)
00242 {
00243 #ifdef HAVE_ATANH
00244        return(atanh(z));
00245 #else
00246        return(0.5 * log((1 + z) / (1 - z)));
00247 #endif
00248 }
00249 /* }}} */
00250 
00251 /* {{{ php_log1p
00252 */
00253 static double php_log1p(double x)
00254 {
00255 #ifdef HAVE_LOG1P
00256        return(log1p(x));
00257 #else
00258        return(log(1 + x));
00259 #endif
00260 }
00261 /* }}} */
00262 
00263 /* {{{ php_expm1
00264 */
00265 static double php_expm1(double x)
00266 {
00267 #if !defined(PHP_WIN32) && !defined(NETWARE)
00268        return(expm1(x));
00269 #else
00270        return(exp(x) - 1);
00271 #endif
00272 }
00273 /* }}}*/
00274 
00275 /* {{{ proto int abs(int number)
00276    Return the absolute value of the number */
00277 PHP_FUNCTION(abs) 
00278 {
00279        zval **value;
00280        
00281        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
00282               return;
00283        }
00284        convert_scalar_to_number_ex(value);
00285        
00286        if (Z_TYPE_PP(value) == IS_DOUBLE) {
00287               RETURN_DOUBLE(fabs(Z_DVAL_PP(value)));
00288        } else if (Z_TYPE_PP(value) == IS_LONG) {
00289               if (Z_LVAL_PP(value) == LONG_MIN) {
00290                      RETURN_DOUBLE(-(double)LONG_MIN);
00291               } else {
00292                      RETURN_LONG(Z_LVAL_PP(value) < 0 ? -Z_LVAL_PP(value) : Z_LVAL_PP(value));
00293               }
00294        }
00295        RETURN_FALSE;
00296 }
00297 /* }}} */ 
00298 
00299 /* {{{ proto float ceil(float number)
00300    Returns the next highest integer value of the number */
00301 PHP_FUNCTION(ceil) 
00302 {
00303        zval **value;
00304        
00305        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
00306               return;
00307        }
00308        convert_scalar_to_number_ex(value);
00309 
00310        if (Z_TYPE_PP(value) == IS_DOUBLE) {
00311               RETURN_DOUBLE(ceil(Z_DVAL_PP(value)));
00312        } else if (Z_TYPE_PP(value) == IS_LONG) {
00313               convert_to_double_ex(value);
00314               RETURN_DOUBLE(Z_DVAL_PP(value));
00315        }
00316        RETURN_FALSE;
00317 }
00318 /* }}} */
00319 
00320 /* {{{ proto float floor(float number)
00321    Returns the next lowest integer value from the number */
00322 PHP_FUNCTION(floor)
00323 {
00324        zval **value;
00325        
00326        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
00327               return;
00328        }
00329        convert_scalar_to_number_ex(value);
00330 
00331        if (Z_TYPE_PP(value) == IS_DOUBLE) {
00332               RETURN_DOUBLE(floor(Z_DVAL_PP(value)));
00333        } else if (Z_TYPE_PP(value) == IS_LONG) {
00334               convert_to_double_ex(value);
00335               RETURN_DOUBLE(Z_DVAL_PP(value));
00336        }
00337        RETURN_FALSE;
00338 }
00339 /* }}} */
00340 
00341 /* {{{ proto float round(float number [, int precision [, int mode]])
00342    Returns the number rounded to specified precision */
00343 PHP_FUNCTION(round)
00344 {
00345        zval **value;
00346        int places = 0;
00347        long precision = 0;
00348        long mode = PHP_ROUND_HALF_UP;
00349        double return_val;
00350        
00351        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|ll", &value, &precision, &mode) == FAILURE) {
00352               return;
00353        }
00354 
00355        if (ZEND_NUM_ARGS() >= 2) {
00356               places = (int) precision;
00357        }
00358        convert_scalar_to_number_ex(value);
00359 
00360        switch (Z_TYPE_PP(value)) {
00361               case IS_LONG:
00362                      /* Simple case - long that doesn't need to be rounded. */
00363                      if (places >= 0) {
00364                             RETURN_DOUBLE((double) Z_LVAL_PP(value));
00365                      }
00366                      /* break omitted intentionally */
00367 
00368               case IS_DOUBLE:
00369                      return_val = (Z_TYPE_PP(value) == IS_LONG) ? (double)Z_LVAL_PP(value) : Z_DVAL_PP(value);
00370                      return_val = _php_math_round(return_val, places, mode);
00371                      RETURN_DOUBLE(return_val);
00372                      break;
00373 
00374               default:
00375                      RETURN_FALSE;
00376                      break;
00377        }
00378 }
00379 /* }}} */
00380 
00381 /* {{{ proto float sin(float number)
00382    Returns the sine of the number in radians */
00383 PHP_FUNCTION(sin)
00384 {
00385        double num;
00386 
00387        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00388               return;
00389        }
00390        RETURN_DOUBLE(sin(num));
00391 }
00392 /* }}} */
00393 
00394 /* {{{ proto float cos(float number)
00395    Returns the cosine of the number in radians */
00396 PHP_FUNCTION(cos)
00397 {
00398        double num;
00399        
00400        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00401               return;
00402        }
00403        RETURN_DOUBLE(cos(num));
00404 }
00405 /* }}} */
00406 
00407 /* {{{ proto float tan(float number)
00408    Returns the tangent of the number in radians */
00409 PHP_FUNCTION(tan)
00410 {
00411        double num;
00412 
00413        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00414               return;
00415        }
00416        RETURN_DOUBLE(tan(num));
00417 }
00418 /* }}} */
00419 
00420 /* {{{ proto float asin(float number)
00421    Returns the arc sine of the number in radians */
00422 PHP_FUNCTION(asin)
00423 {
00424        double num;
00425 
00426        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00427               return;
00428        }
00429        RETURN_DOUBLE(asin(num));
00430 }
00431 /* }}} */
00432 
00433 /* {{{ proto float acos(float number)
00434    Return the arc cosine of the number in radians */
00435 PHP_FUNCTION(acos)
00436 {
00437        double num;
00438 
00439        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00440               return;
00441        }
00442        RETURN_DOUBLE(acos(num));
00443 }
00444 /* }}} */
00445 
00446 /* {{{ proto float atan(float number)
00447    Returns the arc tangent of the number in radians */
00448 PHP_FUNCTION(atan)
00449 {
00450        double num;
00451 
00452        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00453               return;
00454        }
00455        RETURN_DOUBLE(atan(num));
00456 }
00457 /* }}} */
00458 
00459 /* {{{ proto float atan2(float y, float x)
00460    Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
00461 PHP_FUNCTION(atan2)
00462 {
00463        double num1, num2;
00464 
00465        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
00466               return;
00467        }
00468        RETURN_DOUBLE(atan2(num1, num2));
00469 }
00470 /* }}} */
00471 
00472 /* {{{ proto float sinh(float number)
00473    Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
00474 PHP_FUNCTION(sinh)
00475 {
00476        double num;
00477 
00478        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00479               return;
00480        }
00481        RETURN_DOUBLE(sinh(num));
00482 }
00483 /* }}} */
00484 
00485 /* {{{ proto float cosh(float number)
00486    Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
00487 PHP_FUNCTION(cosh)
00488 {
00489        double num;
00490 
00491        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00492               return;
00493        }
00494        RETURN_DOUBLE(cosh(num));
00495 }
00496 /* }}} */
00497 
00498 /* {{{ proto float tanh(float number)
00499    Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
00500 PHP_FUNCTION(tanh)
00501 {
00502        double num;
00503 
00504        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00505               return;
00506        }
00507        RETURN_DOUBLE(tanh(num));
00508 }
00509 /* }}} */
00510 
00511 /* {{{ proto float asinh(float number)
00512    Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
00513 PHP_FUNCTION(asinh)
00514 {
00515        double num;
00516 
00517        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00518               return;
00519        }
00520        RETURN_DOUBLE(php_asinh(num));
00521 }
00522 /* }}} */
00523 
00524 /* {{{ proto float acosh(float number)
00525    Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
00526 PHP_FUNCTION(acosh)
00527 {
00528        double num;
00529        
00530        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00531               return;
00532        }
00533        RETURN_DOUBLE(php_acosh(num));
00534 }
00535 /* }}} */
00536 
00537 /* {{{ proto float atanh(float number)
00538    Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
00539 PHP_FUNCTION(atanh)
00540 {
00541        double num;
00542 
00543        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00544               return;
00545        }
00546        RETURN_DOUBLE(php_atanh(num));
00547 }
00548 /* }}} */
00549 
00550 /* {{{ proto float pi(void)
00551    Returns an approximation of pi */
00552 PHP_FUNCTION(pi)
00553 {
00554        RETURN_DOUBLE(M_PI);
00555 }
00556 /* }}} */
00557 
00558 /* {{{ proto bool is_finite(float val)
00559    Returns whether argument is finite */
00560 PHP_FUNCTION(is_finite)
00561 {
00562        double dval;
00563 
00564        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
00565               return;
00566        }
00567        RETURN_BOOL(zend_finite(dval));
00568 }
00569 /* }}} */
00570 
00571 /* {{{ proto bool is_infinite(float val)
00572    Returns whether argument is infinite */
00573 PHP_FUNCTION(is_infinite)
00574 {
00575        double dval;
00576 
00577        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
00578               return;
00579        }
00580        RETURN_BOOL(zend_isinf(dval));
00581 }
00582 /* }}} */
00583 
00584 /* {{{ proto bool is_nan(float val)
00585    Returns whether argument is not a number */
00586 PHP_FUNCTION(is_nan)
00587 {
00588        double dval;
00589 
00590        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
00591               return;
00592        }
00593        RETURN_BOOL(zend_isnan(dval));
00594 }
00595 /* }}} */
00596 
00597 /* {{{ proto number pow(number base, number exponent)
00598    Returns base raised to the power of exponent. Returns integer result when possible */
00599 PHP_FUNCTION(pow)
00600 {
00601        zval *zbase, *zexp;
00602 
00603        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/", &zbase, &zexp) == FAILURE) {
00604               return;
00605        }
00606 
00607        /* make sure we're dealing with numbers */
00608        convert_scalar_to_number(zbase TSRMLS_CC);
00609        convert_scalar_to_number(zexp TSRMLS_CC);
00610 
00611        /* if both base and exponent were longs, we'll try to get a long out */
00612        if (Z_TYPE_P(zbase) == IS_LONG && Z_TYPE_P(zexp) == IS_LONG && Z_LVAL_P(zexp) >= 0) {
00613               long l1 = 1, l2 = Z_LVAL_P(zbase), i = Z_LVAL_P(zexp);
00614               
00615               if (i == 0) {
00616                      RETURN_LONG(1L);
00617               } else if (l2 == 0) {
00618                      RETURN_LONG(0);
00619               }
00620 
00621               /* calculate pow(long,long) in O(log exp) operations, bail if overflow */
00622               while (i >= 1) {
00623                      int overflow;
00624                      double dval = 0.0;
00625 
00626                      if (i % 2) {
00627                             --i;
00628                             ZEND_SIGNED_MULTIPLY_LONG(l1,l2,l1,dval,overflow);
00629                             if (overflow) RETURN_DOUBLE(dval * pow(l2,i));
00630                      } else {
00631                             i /= 2;
00632                             ZEND_SIGNED_MULTIPLY_LONG(l2,l2,l2,dval,overflow);
00633                             if (overflow) RETURN_DOUBLE((double)l1 * pow(dval,i));
00634                      }
00635                      if (i == 0) {
00636                             RETURN_LONG(l1);
00637                      }
00638               }
00639        }
00640        convert_to_double(zbase);
00641        convert_to_double(zexp);
00642        
00643        RETURN_DOUBLE(pow(Z_DVAL_P(zbase), Z_DVAL_P(zexp)));
00644 }
00645 /* }}} */
00646 
00647 /* {{{ proto float exp(float number)
00648    Returns e raised to the power of the number */
00649 PHP_FUNCTION(exp)
00650 {
00651        double num;
00652 
00653        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00654               return;
00655        }
00656 
00657        RETURN_DOUBLE(exp(num));
00658 }
00659 /* }}} */
00660 
00661 /* {{{ proto float expm1(float number)
00662    Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
00663 /*
00664    WARNING: this function is expermental: it could change its name or 
00665    disappear in the next version of PHP!
00666 */
00667 PHP_FUNCTION(expm1)
00668 {
00669        double num;
00670 
00671        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00672               return;
00673        }
00674        RETURN_DOUBLE(php_expm1(num));
00675 }
00676 /* }}} */
00677 
00678 /* {{{ proto float log1p(float number)
00679    Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */ 
00680 /*
00681    WARNING: this function is expermental: it could change its name or 
00682    disappear in the next version of PHP!
00683 */
00684 PHP_FUNCTION(log1p)
00685 {
00686        double num;
00687 
00688        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00689               return;
00690        }
00691        RETURN_DOUBLE(php_log1p(num));
00692 }
00693 /* }}} */
00694 
00695 /* {{{ proto float log(float number, [float base])
00696    Returns the natural logarithm of the number, or the base log if base is specified */
00697 PHP_FUNCTION(log)
00698 {
00699        double num, base = 0;
00700        
00701        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) == FAILURE) {
00702               return;
00703        }
00704        if (ZEND_NUM_ARGS() == 1) {
00705               RETURN_DOUBLE(log(num));
00706        }
00707        if (base <= 0.0) {
00708               php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");                       
00709               RETURN_FALSE;
00710        }
00711        if (base == 1) {
00712               RETURN_DOUBLE(php_get_nan());
00713        } else {
00714               RETURN_DOUBLE(log(num) / log(base));
00715        }
00716 }
00717 /* }}} */
00718 
00719 /* {{{ proto float log10(float number)
00720    Returns the base-10 logarithm of the number */
00721 PHP_FUNCTION(log10)
00722 {
00723        double num;
00724 
00725        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00726               return;
00727        }
00728        RETURN_DOUBLE(log10(num));
00729 }
00730 /* }}} */
00731 
00732 /* {{{ proto float sqrt(float number)
00733    Returns the square root of the number */
00734 PHP_FUNCTION(sqrt)
00735 {
00736        double num;
00737 
00738        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
00739               return;
00740        }
00741        RETURN_DOUBLE(sqrt(num));
00742 }
00743 /* }}} */
00744 
00745 /* {{{ proto float hypot(float num1, float num2)
00746    Returns sqrt(num1*num1 + num2*num2) */ 
00747 PHP_FUNCTION(hypot)
00748 {
00749        double num1, num2;
00750 
00751        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
00752               return;
00753        }
00754 #if HAVE_HYPOT
00755        RETURN_DOUBLE(hypot(num1, num2));
00756 #elif defined(_MSC_VER)
00757        RETURN_DOUBLE(_hypot(num1, num2));
00758 #else
00759        RETURN_DOUBLE(sqrt((num1 * num1) + (num2 * num2)));
00760 #endif
00761 }
00762 /* }}} */
00763 
00764 /* {{{ proto float deg2rad(float number)
00765    Converts the number in degrees to the radian equivalent */
00766 PHP_FUNCTION(deg2rad)
00767 {
00768        double deg;
00769 
00770        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &deg) == FAILURE) {
00771               return;
00772        }
00773        RETURN_DOUBLE((deg / 180.0) * M_PI);
00774 }
00775 /* }}} */
00776 
00777 /* {{{ proto float rad2deg(float number)
00778    Converts the radian number to the equivalent number in degrees */
00779 PHP_FUNCTION(rad2deg)
00780 {
00781        double rad;
00782 
00783        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &rad) == FAILURE) {
00784               return;
00785        }
00786        RETURN_DOUBLE((rad / M_PI) * 180);
00787 }
00788 /* }}} */
00789 
00790 /* {{{ _php_math_basetolong */
00791 /*
00792  * Convert a string representation of a base(2-36) number to a long.
00793  */
00794 PHPAPI long _php_math_basetolong(zval *arg, int base)
00795 {
00796        long num = 0, digit, onum;
00797        int i;
00798        char c, *s;
00799 
00800        if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
00801               return 0;
00802        }
00803 
00804        s = Z_STRVAL_P(arg);
00805 
00806        for (i = Z_STRLEN_P(arg); i > 0; i--) {
00807               c = *s++;
00808               
00809               digit = (c >= '0' && c <= '9') ? c - '0'
00810                      : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
00811                      : (c >= 'a' && c <= 'z') ? c - 'a' + 10
00812                      : base;
00813               
00814               if (digit >= base) {
00815                      continue;
00816               }
00817 
00818               onum = num;
00819               num = num * base + digit;
00820               if (num > onum)
00821                      continue;
00822 
00823               {
00824                      TSRMLS_FETCH();
00825 
00826                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
00827                      return LONG_MAX;
00828               }
00829        }
00830 
00831        return num;
00832 }
00833 /* }}} */
00834 
00835 /* {{{ _php_math_basetozval */
00836 /*
00837  * Convert a string representation of a base(2-36) number to a zval.
00838  */
00839 PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
00840 {
00841        long num = 0;
00842        double fnum = 0;
00843        int i;
00844        int mode = 0;
00845        char c, *s;
00846        long cutoff;
00847        int cutlim;
00848 
00849        if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
00850               return FAILURE;
00851        }
00852 
00853        s = Z_STRVAL_P(arg);
00854 
00855        cutoff = LONG_MAX / base;
00856        cutlim = LONG_MAX % base;
00857        
00858        for (i = Z_STRLEN_P(arg); i > 0; i--) {
00859               c = *s++;
00860 
00861               /* might not work for EBCDIC */
00862               if (c >= '0' && c <= '9') 
00863                      c -= '0';
00864               else if (c >= 'A' && c <= 'Z') 
00865                      c -= 'A' - 10;
00866               else if (c >= 'a' && c <= 'z') 
00867                      c -= 'a' - 10;
00868               else
00869                      continue;
00870 
00871               if (c >= base)
00872                      continue;
00873               
00874               switch (mode) {
00875               case 0: /* Integer */
00876                      if (num < cutoff || (num == cutoff && c <= cutlim)) {
00877                             num = num * base + c;
00878                             break;
00879                      } else {
00880                             fnum = num;
00881                             mode = 1;
00882                      }
00883                      /* fall-through */
00884               case 1: /* Float */
00885                      fnum = fnum * base + c;
00886               }      
00887        }
00888 
00889        if (mode == 1) {
00890               ZVAL_DOUBLE(ret, fnum);
00891        } else {
00892               ZVAL_LONG(ret, num);
00893        }
00894        return SUCCESS;
00895 }
00896 /* }}} */
00897 
00898 /* {{{ _php_math_longtobase */
00899 /*
00900  * Convert a long to a string containing a base(2-36) representation of
00901  * the number.
00902  */
00903 PHPAPI char * _php_math_longtobase(zval *arg, int base)
00904 {
00905        static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
00906        char buf[(sizeof(unsigned long) << 3) + 1];
00907        char *ptr, *end;
00908        unsigned long value;
00909 
00910        if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
00911               return STR_EMPTY_ALLOC();
00912        }
00913 
00914        value = Z_LVAL_P(arg);
00915 
00916        end = ptr = buf + sizeof(buf) - 1;
00917        *ptr = '\0';
00918 
00919        do {
00920               *--ptr = digits[value % base];
00921               value /= base;
00922        } while (ptr > buf && value);
00923 
00924        return estrndup(ptr, end - ptr);
00925 }
00926 /* }}} */
00927 
00928 /* {{{ _php_math_zvaltobase */
00929 /*
00930  * Convert a zval to a string containing a base(2-36) representation of
00931  * the number.
00932  */
00933 PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
00934 {
00935        static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
00936 
00937        if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
00938               return STR_EMPTY_ALLOC();
00939        }
00940 
00941        if (Z_TYPE_P(arg) == IS_DOUBLE) {
00942               double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
00943               char *ptr, *end;
00944               char buf[(sizeof(double) << 3) + 1];
00945 
00946               /* Don't try to convert +/- infinity */
00947               if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
00948                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
00949                      return STR_EMPTY_ALLOC();
00950               }
00951 
00952               end = ptr = buf + sizeof(buf) - 1;
00953               *ptr = '\0';
00954 
00955               do {
00956                      *--ptr = digits[(int) fmod(fvalue, base)];
00957                      fvalue /= base;
00958               } while (ptr > buf && fabs(fvalue) >= 1);
00959 
00960               return estrndup(ptr, end - ptr);
00961        }
00962        
00963        return _php_math_longtobase(arg, base);
00964 }      
00965 /* }}} */
00966 
00967 /* {{{ proto int bindec(string binary_number)
00968    Returns the decimal equivalent of the binary number */
00969 PHP_FUNCTION(bindec)
00970 {
00971        zval **arg;
00972        
00973        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
00974               return;
00975        }
00976        convert_to_string_ex(arg);
00977        if (_php_math_basetozval(*arg, 2, return_value) == FAILURE) {
00978               RETURN_FALSE;
00979        }
00980 }
00981 /* }}} */
00982 
00983 /* {{{ proto int hexdec(string hexadecimal_number)
00984    Returns the decimal equivalent of the hexadecimal number */
00985 PHP_FUNCTION(hexdec)
00986 {
00987        zval **arg;
00988        
00989        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
00990               return;
00991        }
00992        convert_to_string_ex(arg);
00993        if (_php_math_basetozval(*arg, 16, return_value) == FAILURE) {
00994               RETURN_FALSE;
00995        }
00996 }
00997 /* }}} */
00998 
00999 /* {{{ proto int octdec(string octal_number)
01000    Returns the decimal equivalent of an octal string */
01001 PHP_FUNCTION(octdec)
01002 {
01003        zval **arg;
01004        
01005        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
01006               return;
01007        }
01008        convert_to_string_ex(arg);
01009        if (_php_math_basetozval(*arg, 8, return_value) == FAILURE) {
01010               RETURN_FALSE;
01011        }
01012 }
01013 /* }}} */
01014 
01015 /* {{{ proto string decbin(int decimal_number)
01016    Returns a string containing a binary representation of the number */
01017 PHP_FUNCTION(decbin)
01018 {
01019        zval **arg;
01020        char *result;
01021 
01022        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
01023               return;
01024        }
01025        convert_to_long_ex(arg);
01026        result = _php_math_longtobase(*arg, 2);
01027        RETURN_STRING(result, 0);
01028 }
01029 /* }}} */
01030 
01031 /* {{{ proto string decoct(int decimal_number)
01032    Returns a string containing an octal representation of the given number */
01033 PHP_FUNCTION(decoct)
01034 {
01035        zval **arg;
01036        char *result;
01037 
01038        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
01039               return;
01040        }
01041        convert_to_long_ex(arg);
01042        result = _php_math_longtobase(*arg, 8);
01043        RETURN_STRING(result, 0);
01044 }
01045 /* }}} */
01046 
01047 /* {{{ proto string dechex(int decimal_number)
01048    Returns a string containing a hexadecimal representation of the given number */
01049 PHP_FUNCTION(dechex)
01050 {
01051        zval **arg;
01052        char *result;
01053 
01054        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
01055               return;
01056        }
01057        convert_to_long_ex(arg);
01058        result = _php_math_longtobase(*arg, 16);
01059        RETURN_STRING(result, 0);
01060 }
01061 /* }}} */
01062 
01063 /* {{{ proto string base_convert(string number, int frombase, int tobase)
01064    Converts a number in a string from any base <= 36 to any base <= 36 */
01065 PHP_FUNCTION(base_convert)
01066 {
01067        zval **number, temp;
01068        long frombase, tobase;
01069        char *result;
01070 
01071        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zll", &number, &frombase, &tobase) == FAILURE) {
01072               return;
01073        }
01074        convert_to_string_ex(number);
01075        
01076        if (frombase < 2 || frombase > 36) {
01077               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", frombase);
01078               RETURN_FALSE;
01079        }
01080        if (tobase < 2 || tobase > 36) {
01081               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", tobase);
01082               RETURN_FALSE;
01083        }
01084 
01085        if(_php_math_basetozval(*number, frombase, &temp) == FAILURE) {
01086               RETURN_FALSE;
01087        }
01088        result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
01089        RETVAL_STRING(result, 0);
01090 } 
01091 /* }}} */
01092 
01093 /* {{{ _php_math_number_format 
01094 */
01095 PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
01096 {
01097        char *tmpbuf = NULL, *resbuf;
01098        char *s, *t;  /* source, target */
01099        char *dp;
01100        int integral;
01101        int tmplen, reslen=0;
01102        int count=0;
01103        int is_negative=0;
01104 
01105        if (d < 0) {
01106               is_negative = 1;
01107               d = -d;
01108        }
01109 
01110        dec = MAX(0, dec);
01111        d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
01112 
01113        tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d);
01114 
01115        if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
01116               return tmpbuf;
01117        }
01118 
01119        /* find decimal point, if expected */
01120        if (dec) {
01121               dp = strpbrk(tmpbuf, ".,");
01122        } else {
01123               dp = NULL;
01124        }
01125 
01126        /* calculate the length of the return buffer */
01127        if (dp) {
01128               integral = dp - tmpbuf;
01129        } else {
01130               /* no decimal point was found */
01131               integral = tmplen;
01132        }
01133 
01134        /* allow for thousand separators */
01135        if (thousand_sep) {
01136               integral += (integral-1) / 3;
01137        }
01138        
01139        reslen = integral;
01140        
01141        if (dec) {
01142               reslen += dec;
01143 
01144               if (dec_point) {
01145                      reslen++;
01146               }
01147        }
01148 
01149        /* add a byte for minus sign */
01150        if (is_negative) {
01151               reslen++;
01152        }
01153        resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */
01154 
01155        s = tmpbuf+tmplen-1;
01156        t = resbuf+reslen;
01157        *t-- = '\0';
01158 
01159        /* copy the decimal places.
01160         * Take care, as the sprintf implementation may return less places than
01161         * we requested due to internal buffer limitations */
01162        if (dec) {
01163               int declen = dp ? s - dp : 0;
01164               int topad = dec > declen ? dec - declen : 0;
01165 
01166               /* pad with '0's */
01167               while (topad--) {
01168                      *t-- = '0';
01169               }
01170               
01171               if (dp) {
01172                      s -= declen + 1; /* +1 to skip the point */
01173                      t -= declen;
01174 
01175                      /* now copy the chars after the point */
01176                      memcpy(t + 1, dp + 1, declen);
01177               }
01178 
01179               /* add decimal point */
01180               if (dec_point) {
01181                      *t-- = dec_point;
01182               }
01183        }
01184 
01185        /* copy the numbers before the decimal point, adding thousand
01186         * separator every three digits */
01187        while(s >= tmpbuf) {
01188               *t-- = *s--;
01189               if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
01190                      *t-- = thousand_sep;
01191               }
01192        }
01193 
01194        /* and a minus sign, if needed */
01195        if (is_negative) {
01196               *t-- = '-';
01197        }
01198 
01199        efree(tmpbuf);
01200        
01201        return resbuf;
01202 }
01203 /* }}} */
01204 
01205 /* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_seperator, string thousands_seperator]])
01206    Formats a number with grouped thousands */
01207 PHP_FUNCTION(number_format)
01208 {
01209        double num;
01210        long dec = 0;
01211        char *thousand_sep = NULL, *dec_point = NULL;
01212        char thousand_sep_chr = ',', dec_point_chr = '.';
01213        int thousand_sep_len = 0, dec_point_len = 0;
01214        
01215        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|ls!s!", &num, &dec, &dec_point, &dec_point_len, &thousand_sep, &thousand_sep_len) == FAILURE) {
01216               return;
01217        }
01218 
01219        switch(ZEND_NUM_ARGS()) {
01220        case 1:
01221               RETURN_STRING(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr), 0);
01222               break;
01223        case 2:
01224               RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
01225               break;
01226        case 4:
01227               if (dec_point != NULL) {
01228                      if (dec_point_len) {
01229                             dec_point_chr = dec_point[0];
01230                      } else {
01231                             dec_point_chr = 0;
01232                      }
01233               }
01234               if (thousand_sep != NULL) {
01235                      if (thousand_sep_len) {
01236                             thousand_sep_chr = thousand_sep[0];
01237                      } else {
01238                             thousand_sep_chr = 0;       
01239                      }
01240               }
01241               RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
01242               break;
01243        default:
01244               WRONG_PARAM_COUNT;
01245               break;
01246        }
01247 }
01248 /* }}} */
01249 
01250 /* {{{ proto float fmod(float x, float y)
01251    Returns the remainder of dividing x by y as a float */
01252 PHP_FUNCTION(fmod)
01253 {
01254        double num1, num2;
01255 
01256        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd",  &num1, &num2) == FAILURE) {
01257               return;
01258        }
01259        RETURN_DOUBLE(fmod(num1, num2));
01260 }
01261 /* }}} */
01262 
01263 
01264 
01265 /*
01266  * Local variables:
01267  * tab-width: 4
01268  * c-basic-offset: 4
01269  * End:
01270  * vim600: fdm=marker
01271  * vim: noet sw=4 ts=4
01272  */