root/ext/standard/math.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_intlog10abs
  2. php_intpow10
  3. php_math_is_finite
  4. php_round_helper
  5. _php_math_round
  6. php_asinh
  7. php_acosh
  8. php_atanh
  9. php_log1p
  10. php_expm1
  11. PHP_FUNCTION
  12. PHP_FUNCTION
  13. PHP_FUNCTION
  14. PHP_FUNCTION
  15. PHP_FUNCTION
  16. PHP_FUNCTION
  17. PHP_FUNCTION
  18. PHP_FUNCTION
  19. PHP_FUNCTION
  20. PHP_FUNCTION
  21. PHP_FUNCTION
  22. PHP_FUNCTION
  23. PHP_FUNCTION
  24. PHP_FUNCTION
  25. PHP_FUNCTION
  26. PHP_FUNCTION
  27. PHP_FUNCTION
  28. PHP_FUNCTION
  29. PHP_FUNCTION
  30. PHP_FUNCTION
  31. PHP_FUNCTION
  32. PHP_FUNCTION
  33. PHP_FUNCTION
  34. PHP_FUNCTION
  35. PHP_FUNCTION
  36. PHP_FUNCTION
  37. PHP_FUNCTION
  38. PHP_FUNCTION
  39. PHP_FUNCTION
  40. PHP_FUNCTION
  41. PHP_FUNCTION
  42. _php_math_basetolong
  43. _php_math_basetozval
  44. _php_math_longtobase
  45. _php_math_zvaltobase
  46. PHP_FUNCTION
  47. PHP_FUNCTION
  48. PHP_FUNCTION
  49. PHP_FUNCTION
  50. PHP_FUNCTION
  51. PHP_FUNCTION
  52. PHP_FUNCTION
  53. _php_math_number_format
  54. _php_math_number_format_ex_len
  55. _php_math_number_format_ex
  56. PHP_FUNCTION
  57. PHP_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 5                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Authors: Jim Winstead <jimw@php.net>                                 |
  16    |          Stig Sæther Bakken <ssb@php.net>                            |
  17    |          Zeev Suraski <zeev@zend.com>                                |
  18    | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net>               |
  19    +----------------------------------------------------------------------+
  20 */
  21 
  22 /* $Id$ */
  23 
  24 #include "php.h"
  25 #include "php_math.h"
  26 #include "zend_multiply.h"
  27 
  28 #include <math.h>
  29 #include <float.h>
  30 #include <stdlib.h>
  31 
  32 #include "basic_functions.h"
  33 
  34 /* {{{ php_intlog10abs
  35    Returns floor(log10(fabs(val))), uses fast binary search */
  36 static inline int php_intlog10abs(double value) {
  37         int result;
  38         value = fabs(value);
  39 
  40         if (value < 1e-8 || value > 1e22) {
  41                 result = (int)floor(log10(value));
  42         } else {
  43                 static const double values[] = {
  44                         1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
  45                         1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
  46                         1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
  47                         1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
  48                 /* Do a binary search with 5 steps */
  49                 result = 15;
  50                 if (value < values[result]) {
  51                         result -= 8;
  52                 } else {
  53                         result += 8;
  54                 }
  55                 if (value < values[result]) {
  56                         result -= 4;
  57                 } else {
  58                         result += 4;
  59                 }
  60                 if (value < values[result]) {
  61                         result -= 2;
  62                 } else {
  63                         result += 2;
  64                 }
  65                 if (value < values[result]) {
  66                         result -= 1;
  67                 } else {
  68                         result += 1;
  69                 }
  70                 if (value < values[result]) {
  71                         result -= 1;
  72                 }
  73                 result -= 8;
  74         }
  75         return result;
  76 }
  77 /* }}} */
  78 
  79 /* {{{ php_intpow10
  80        Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
  81 static inline double php_intpow10(int power) {
  82         static const double powers[] = {
  83                 1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
  84                 1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
  85                 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
  86 
  87         /* Not in lookup table */
  88         if (power < 0 || power > 22) {
  89                 return pow(10.0, (double)power);
  90         }
  91         return powers[power];
  92 }
  93 /* }}} */
  94 
  95 /* {{{ php_math_is_finite */
  96 static inline int php_math_is_finite(double value) {
  97 #if defined(PHP_WIN32)
  98         return _finite(value);
  99 #elif defined(isfinite)
 100         return isfinite(value);
 101 #else
 102         return value == value && (value == 0. || value * 2. != value);
 103 #endif
 104 }
 105 /* }}} */
 106 
 107 /* {{{ php_round_helper
 108        Actually performs the rounding of a value to integer in a certain mode */
 109 static inline double php_round_helper(double value, int mode) {
 110         double tmp_value;
 111 
 112         if (value >= 0.0) {
 113                 tmp_value = floor(value + 0.5);
 114                 if ((mode == PHP_ROUND_HALF_DOWN && value == (-0.5 + tmp_value)) ||
 115                         (mode == PHP_ROUND_HALF_EVEN && value == (0.5 + 2 * floor(tmp_value/2.0))) ||
 116                         (mode == PHP_ROUND_HALF_ODD  && value == (0.5 + 2 * floor(tmp_value/2.0) - 1.0)))
 117                 {
 118                         tmp_value = tmp_value - 1.0;
 119                 }
 120         } else {
 121                 tmp_value = ceil(value - 0.5);
 122                 if ((mode == PHP_ROUND_HALF_DOWN && value == (0.5 + tmp_value)) ||
 123                         (mode == PHP_ROUND_HALF_EVEN && value == (-0.5 + 2 * ceil(tmp_value/2.0))) ||
 124                         (mode == PHP_ROUND_HALF_ODD  && value == (-0.5 + 2 * ceil(tmp_value/2.0) + 1.0)))
 125                 {
 126                         tmp_value = tmp_value + 1.0;
 127                 }
 128         }
 129 
 130         return tmp_value;
 131 }
 132 /* }}} */
 133 
 134 /* {{{ _php_math_round */
 135 /*
 136  * Rounds a number to a certain number of decimal places in a certain rounding
 137  * mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding
 138  */
 139 PHPAPI double _php_math_round(double value, int places, int mode) {
 140         double f1, f2;
 141         double tmp_value;
 142         int precision_places;
 143 
 144         if (!php_math_is_finite(value)) {
 145                 return value;
 146         }
 147         
 148         places = places < INT_MIN+1 ? INT_MIN+1 : places;
 149         precision_places = 14 - php_intlog10abs(value);
 150 
 151         f1 = php_intpow10(abs(places));
 152 
 153         /* If the decimal precision guaranteed by FP arithmetic is higher than
 154            the requested places BUT is small enough to make sure a non-zero value
 155            is returned, pre-round the result to the precision */
 156         if (precision_places > places && precision_places - places < 15) {
 157                 int64_t use_precision = precision_places < INT_MIN+1 ? INT_MIN+1 : precision_places;
 158 
 159                 f2 = php_intpow10(abs((int)use_precision));
 160                 if (use_precision >= 0) {
 161                         tmp_value = value * f2;
 162                 } else {
 163                         tmp_value = value / f2;
 164                 }
 165                 /* preround the result (tmp_value will always be something * 1e14,
 166                    thus never larger than 1e15 here) */
 167                 tmp_value = php_round_helper(tmp_value, mode);
 168 
 169                 use_precision = places - precision_places;
 170                 use_precision = use_precision < INT_MIN+1 ? INT_MIN+1 : use_precision;
 171                 /* now correctly move the decimal point */
 172                 f2 = php_intpow10(abs((int)use_precision));
 173                 /* because places < precision_places */
 174                 tmp_value = tmp_value / f2;
 175         } else {
 176                 /* adjust the value */
 177                 if (places >= 0) {
 178                         tmp_value = value * f1;
 179                 } else {
 180                         tmp_value = value / f1;
 181                 }
 182                 /* This value is beyond our precision, so rounding it is pointless */
 183                 if (fabs(tmp_value) >= 1e15) {
 184                         return value;
 185                 }
 186         }
 187 
 188         /* round the temp value */
 189         tmp_value = php_round_helper(tmp_value, mode);
 190         
 191         /* see if it makes sense to use simple division to round the value */
 192         if (abs(places) < 23) {
 193                 if (places > 0) {
 194                         tmp_value = tmp_value / f1;
 195                 } else {
 196                         tmp_value = tmp_value * f1;
 197                 }
 198         } else {
 199                 /* Simple division can't be used since that will cause wrong results.
 200                    Instead, the number is converted to a string and back again using
 201                    strtod(). strtod() will return the nearest possible FP value for
 202                    that string. */
 203 
 204                 /* 40 Bytes should be more than enough for this format string. The
 205                    float won't be larger than 1e15 anyway. But just in case, use
 206                    snprintf() and make sure the buffer is zero-terminated */
 207                 char buf[40];
 208                 snprintf(buf, 39, "%15fe%d", tmp_value, -places);
 209                 buf[39] = '\0';
 210                 tmp_value = zend_strtod(buf, NULL);
 211                 /* couldn't convert to string and back */
 212                 if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
 213                         tmp_value = value;
 214                 }
 215         }
 216 
 217         return tmp_value;
 218 }
 219 /* }}} */
 220 
 221 /* {{{ php_asinh
 222 */
 223 static double php_asinh(double z)
 224 {
 225 #ifdef HAVE_ASINH
 226         return(asinh(z));
 227 #else
 228         return(log(z + sqrt(1 + pow(z, 2))) / log(M_E));
 229 #endif
 230 }
 231 /* }}} */
 232 
 233 /* {{{ php_acosh
 234 */
 235 static double php_acosh(double x)
 236 {
 237 #ifdef HAVE_ACOSH
 238         return(acosh(x));
 239 #else
 240         return(log(x + sqrt(x * x - 1)));
 241 #endif
 242 }
 243 /* }}} */
 244 
 245 /* {{{ php_atanh
 246 */
 247 static double php_atanh(double z)
 248 {
 249 #ifdef HAVE_ATANH
 250         return(atanh(z));
 251 #else
 252         return(0.5 * log((1 + z) / (1 - z)));
 253 #endif
 254 }
 255 /* }}} */
 256 
 257 /* {{{ php_log1p
 258 */
 259 static double php_log1p(double x)
 260 {
 261 #ifdef HAVE_LOG1P
 262         return(log1p(x));
 263 #else
 264         return(log(1 + x));
 265 #endif
 266 }
 267 /* }}} */
 268 
 269 /* {{{ php_expm1
 270 */
 271 static double php_expm1(double x)
 272 {
 273 #if !defined(PHP_WIN32) && !defined(NETWARE)
 274         return(expm1(x));
 275 #else
 276         return(exp(x) - 1);
 277 #endif
 278 }
 279 /* }}}*/
 280 
 281 /* {{{ proto int abs(int number)
 282    Return the absolute value of the number */
 283 PHP_FUNCTION(abs) 
 284 {
 285         zval **value;
 286         
 287         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
 288                 return;
 289         }
 290         convert_scalar_to_number_ex(value);
 291         
 292         if (Z_TYPE_PP(value) == IS_DOUBLE) {
 293                 RETURN_DOUBLE(fabs(Z_DVAL_PP(value)));
 294         } else if (Z_TYPE_PP(value) == IS_LONG) {
 295                 if (Z_LVAL_PP(value) == LONG_MIN) {
 296                         RETURN_DOUBLE(-(double)LONG_MIN);
 297                 } else {
 298                         RETURN_LONG(Z_LVAL_PP(value) < 0 ? -Z_LVAL_PP(value) : Z_LVAL_PP(value));
 299                 }
 300         }
 301         RETURN_FALSE;
 302 }
 303 /* }}} */ 
 304 
 305 /* {{{ proto float ceil(float number)
 306    Returns the next highest integer value of the number */
 307 PHP_FUNCTION(ceil) 
 308 {
 309         zval **value;
 310         
 311         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
 312                 return;
 313         }
 314         convert_scalar_to_number_ex(value);
 315 
 316         if (Z_TYPE_PP(value) == IS_DOUBLE) {
 317                 RETURN_DOUBLE(ceil(Z_DVAL_PP(value)));
 318         } else if (Z_TYPE_PP(value) == IS_LONG) {
 319                 convert_to_double_ex(value);
 320                 RETURN_DOUBLE(Z_DVAL_PP(value));
 321         }
 322         RETURN_FALSE;
 323 }
 324 /* }}} */
 325 
 326 /* {{{ proto float floor(float number)
 327    Returns the next lowest integer value from the number */
 328 PHP_FUNCTION(floor)
 329 {
 330         zval **value;
 331         
 332         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
 333                 return;
 334         }
 335         convert_scalar_to_number_ex(value);
 336 
 337         if (Z_TYPE_PP(value) == IS_DOUBLE) {
 338                 RETURN_DOUBLE(floor(Z_DVAL_PP(value)));
 339         } else if (Z_TYPE_PP(value) == IS_LONG) {
 340                 convert_to_double_ex(value);
 341                 RETURN_DOUBLE(Z_DVAL_PP(value));
 342         }
 343         RETURN_FALSE;
 344 }
 345 /* }}} */
 346 
 347 /* {{{ proto float round(float number [, int precision [, int mode]])
 348    Returns the number rounded to specified precision */
 349 PHP_FUNCTION(round)
 350 {
 351         zval **value;
 352         int places = 0;
 353         long precision = 0;
 354         long mode = PHP_ROUND_HALF_UP;
 355         double return_val;
 356         
 357         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|ll", &value, &precision, &mode) == FAILURE) {
 358                 return;
 359         }
 360 
 361         if (ZEND_NUM_ARGS() >= 2) {
 362 #if SIZEOF_LONG > SIZEOF_INT
 363                 if (precision >= 0) {
 364                         places = precision > INT_MAX ? INT_MAX : (int)precision;
 365                 } else {
 366                         places = precision <= INT_MIN ? INT_MIN+1 : (int)precision;
 367                 }
 368 #else
 369                 places = precision;
 370 #endif
 371         }
 372         convert_scalar_to_number_ex(value);
 373 
 374         switch (Z_TYPE_PP(value)) {
 375                 case IS_LONG:
 376                         /* Simple case - long that doesn't need to be rounded. */
 377                         if (places >= 0) {
 378                                 RETURN_DOUBLE((double) Z_LVAL_PP(value));
 379                         }
 380                         /* break omitted intentionally */
 381 
 382                 case IS_DOUBLE:
 383                         return_val = (Z_TYPE_PP(value) == IS_LONG) ? (double)Z_LVAL_PP(value) : Z_DVAL_PP(value);
 384                         return_val = _php_math_round(return_val, places, mode);
 385                         RETURN_DOUBLE(return_val);
 386                         break;
 387 
 388                 default:
 389                         RETURN_FALSE;
 390                         break;
 391         }
 392 }
 393 /* }}} */
 394 
 395 /* {{{ proto float sin(float number)
 396    Returns the sine of the number in radians */
 397 PHP_FUNCTION(sin)
 398 {
 399         double num;
 400 
 401         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 402                 return;
 403         }
 404         RETURN_DOUBLE(sin(num));
 405 }
 406 /* }}} */
 407 
 408 /* {{{ proto float cos(float number)
 409    Returns the cosine of the number in radians */
 410 PHP_FUNCTION(cos)
 411 {
 412         double num;
 413         
 414         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 415                 return;
 416         }
 417         RETURN_DOUBLE(cos(num));
 418 }
 419 /* }}} */
 420 
 421 /* {{{ proto float tan(float number)
 422    Returns the tangent of the number in radians */
 423 PHP_FUNCTION(tan)
 424 {
 425         double num;
 426 
 427         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 428                 return;
 429         }
 430         RETURN_DOUBLE(tan(num));
 431 }
 432 /* }}} */
 433 
 434 /* {{{ proto float asin(float number)
 435    Returns the arc sine of the number in radians */
 436 PHP_FUNCTION(asin)
 437 {
 438         double num;
 439 
 440         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 441                 return;
 442         }
 443         RETURN_DOUBLE(asin(num));
 444 }
 445 /* }}} */
 446 
 447 /* {{{ proto float acos(float number)
 448    Return the arc cosine of the number in radians */
 449 PHP_FUNCTION(acos)
 450 {
 451         double num;
 452 
 453         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 454                 return;
 455         }
 456         RETURN_DOUBLE(acos(num));
 457 }
 458 /* }}} */
 459 
 460 /* {{{ proto float atan(float number)
 461    Returns the arc tangent of the number in radians */
 462 PHP_FUNCTION(atan)
 463 {
 464         double num;
 465 
 466         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 467                 return;
 468         }
 469         RETURN_DOUBLE(atan(num));
 470 }
 471 /* }}} */
 472 
 473 /* {{{ proto float atan2(float y, float x)
 474    Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
 475 PHP_FUNCTION(atan2)
 476 {
 477         double num1, num2;
 478 
 479         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
 480                 return;
 481         }
 482         RETURN_DOUBLE(atan2(num1, num2));
 483 }
 484 /* }}} */
 485 
 486 /* {{{ proto float sinh(float number)
 487    Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
 488 PHP_FUNCTION(sinh)
 489 {
 490         double num;
 491 
 492         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 493                 return;
 494         }
 495         RETURN_DOUBLE(sinh(num));
 496 }
 497 /* }}} */
 498 
 499 /* {{{ proto float cosh(float number)
 500    Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
 501 PHP_FUNCTION(cosh)
 502 {
 503         double num;
 504 
 505         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 506                 return;
 507         }
 508         RETURN_DOUBLE(cosh(num));
 509 }
 510 /* }}} */
 511 
 512 /* {{{ proto float tanh(float number)
 513    Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
 514 PHP_FUNCTION(tanh)
 515 {
 516         double num;
 517 
 518         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 519                 return;
 520         }
 521         RETURN_DOUBLE(tanh(num));
 522 }
 523 /* }}} */
 524 
 525 /* {{{ proto float asinh(float number)
 526    Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
 527 PHP_FUNCTION(asinh)
 528 {
 529         double num;
 530 
 531         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 532                 return;
 533         }
 534         RETURN_DOUBLE(php_asinh(num));
 535 }
 536 /* }}} */
 537 
 538 /* {{{ proto float acosh(float number)
 539    Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
 540 PHP_FUNCTION(acosh)
 541 {
 542         double num;
 543         
 544         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 545                 return;
 546         }
 547         RETURN_DOUBLE(php_acosh(num));
 548 }
 549 /* }}} */
 550 
 551 /* {{{ proto float atanh(float number)
 552    Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
 553 PHP_FUNCTION(atanh)
 554 {
 555         double num;
 556 
 557         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 558                 return;
 559         }
 560         RETURN_DOUBLE(php_atanh(num));
 561 }
 562 /* }}} */
 563 
 564 /* {{{ proto float pi(void)
 565    Returns an approximation of pi */
 566 PHP_FUNCTION(pi)
 567 {
 568         RETURN_DOUBLE(M_PI);
 569 }
 570 /* }}} */
 571 
 572 /* {{{ proto bool is_finite(float val)
 573    Returns whether argument is finite */
 574 PHP_FUNCTION(is_finite)
 575 {
 576         double dval;
 577 
 578         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
 579                 return;
 580         }
 581         RETURN_BOOL(zend_finite(dval));
 582 }
 583 /* }}} */
 584 
 585 /* {{{ proto bool is_infinite(float val)
 586    Returns whether argument is infinite */
 587 PHP_FUNCTION(is_infinite)
 588 {
 589         double dval;
 590 
 591         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
 592                 return;
 593         }
 594         RETURN_BOOL(zend_isinf(dval));
 595 }
 596 /* }}} */
 597 
 598 /* {{{ proto bool is_nan(float val)
 599    Returns whether argument is not a number */
 600 PHP_FUNCTION(is_nan)
 601 {
 602         double dval;
 603 
 604         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
 605                 return;
 606         }
 607         RETURN_BOOL(zend_isnan(dval));
 608 }
 609 /* }}} */
 610 
 611 /* {{{ proto number pow(number base, number exponent)
 612    Returns base raised to the power of exponent. Returns integer result when possible */
 613 PHP_FUNCTION(pow)
 614 {
 615         zval *zbase, *zexp;
 616 
 617         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/", &zbase, &zexp) == FAILURE) {
 618                 return;
 619         }
 620 
 621         pow_function(return_value, zbase, zexp TSRMLS_CC);
 622 }
 623 /* }}} */
 624 
 625 /* {{{ proto float exp(float number)
 626    Returns e raised to the power of the number */
 627 PHP_FUNCTION(exp)
 628 {
 629         double num;
 630 
 631         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 632                 return;
 633         }
 634 
 635         RETURN_DOUBLE(exp(num));
 636 }
 637 /* }}} */
 638 
 639 /* {{{ proto float expm1(float number)
 640    Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
 641 /*
 642    WARNING: this function is expermental: it could change its name or 
 643    disappear in the next version of PHP!
 644 */
 645 PHP_FUNCTION(expm1)
 646 {
 647         double num;
 648 
 649         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 650                 return;
 651         }
 652         RETURN_DOUBLE(php_expm1(num));
 653 }
 654 /* }}} */
 655 
 656 /* {{{ proto float log1p(float number)
 657    Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */ 
 658 /*
 659    WARNING: this function is expermental: it could change its name or 
 660    disappear in the next version of PHP!
 661 */
 662 PHP_FUNCTION(log1p)
 663 {
 664         double num;
 665 
 666         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 667                 return;
 668         }
 669         RETURN_DOUBLE(php_log1p(num));
 670 }
 671 /* }}} */
 672 
 673 /* {{{ proto float log(float number, [float base])
 674    Returns the natural logarithm of the number, or the base log if base is specified */
 675 PHP_FUNCTION(log)
 676 {
 677         double num, base = 0;
 678         
 679         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) == FAILURE) {
 680                 return;
 681         }
 682         if (ZEND_NUM_ARGS() == 1) {
 683                 RETURN_DOUBLE(log(num));
 684         }
 685         if (base <= 0.0) {
 686                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");                             
 687                 RETURN_FALSE;
 688         }
 689         if (base == 1) {
 690                 RETURN_DOUBLE(php_get_nan());
 691         } else {
 692                 RETURN_DOUBLE(log(num) / log(base));
 693         }
 694 }
 695 /* }}} */
 696 
 697 /* {{{ proto float log10(float number)
 698    Returns the base-10 logarithm of the number */
 699 PHP_FUNCTION(log10)
 700 {
 701         double num;
 702 
 703         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 704                 return;
 705         }
 706         RETURN_DOUBLE(log10(num));
 707 }
 708 /* }}} */
 709 
 710 /* {{{ proto float sqrt(float number)
 711    Returns the square root of the number */
 712 PHP_FUNCTION(sqrt)
 713 {
 714         double num;
 715 
 716         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
 717                 return;
 718         }
 719         RETURN_DOUBLE(sqrt(num));
 720 }
 721 /* }}} */
 722 
 723 /* {{{ proto float hypot(float num1, float num2)
 724    Returns sqrt(num1*num1 + num2*num2) */ 
 725 PHP_FUNCTION(hypot)
 726 {
 727         double num1, num2;
 728 
 729         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
 730                 return;
 731         }
 732 #if HAVE_HYPOT
 733         RETURN_DOUBLE(hypot(num1, num2));
 734 #elif defined(_MSC_VER)
 735         RETURN_DOUBLE(_hypot(num1, num2));
 736 #else
 737         RETURN_DOUBLE(sqrt((num1 * num1) + (num2 * num2)));
 738 #endif
 739 }
 740 /* }}} */
 741 
 742 /* {{{ proto float deg2rad(float number)
 743    Converts the number in degrees to the radian equivalent */
 744 PHP_FUNCTION(deg2rad)
 745 {
 746         double deg;
 747 
 748         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &deg) == FAILURE) {
 749                 return;
 750         }
 751         RETURN_DOUBLE((deg / 180.0) * M_PI);
 752 }
 753 /* }}} */
 754 
 755 /* {{{ proto float rad2deg(float number)
 756    Converts the radian number to the equivalent number in degrees */
 757 PHP_FUNCTION(rad2deg)
 758 {
 759         double rad;
 760 
 761         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &rad) == FAILURE) {
 762                 return;
 763         }
 764         RETURN_DOUBLE((rad / M_PI) * 180);
 765 }
 766 /* }}} */
 767 
 768 /* {{{ _php_math_basetolong */
 769 /*
 770  * Convert a string representation of a base(2-36) number to a long.
 771  */
 772 PHPAPI long _php_math_basetolong(zval *arg, int base)
 773 {
 774         long num = 0, digit, onum;
 775         int i;
 776         char c, *s;
 777 
 778         if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
 779                 return 0;
 780         }
 781 
 782         s = Z_STRVAL_P(arg);
 783 
 784         for (i = Z_STRLEN_P(arg); i > 0; i--) {
 785                 c = *s++;
 786                 
 787                 digit = (c >= '0' && c <= '9') ? c - '0'
 788                         : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
 789                         : (c >= 'a' && c <= 'z') ? c - 'a' + 10
 790                         : base;
 791                 
 792                 if (digit >= base) {
 793                         continue;
 794                 }
 795 
 796                 onum = num;
 797                 num = num * base + digit;
 798                 if (num > onum)
 799                         continue;
 800 
 801                 {
 802                         TSRMLS_FETCH();
 803 
 804                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
 805                         return LONG_MAX;
 806                 }
 807         }
 808 
 809         return num;
 810 }
 811 /* }}} */
 812 
 813 /* {{{ _php_math_basetozval */
 814 /*
 815  * Convert a string representation of a base(2-36) number to a zval.
 816  */
 817 PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
 818 {
 819         long num = 0;
 820         double fnum = 0;
 821         int i;
 822         int mode = 0;
 823         char c, *s;
 824         long cutoff;
 825         int cutlim;
 826 
 827         if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
 828                 return FAILURE;
 829         }
 830 
 831         s = Z_STRVAL_P(arg);
 832 
 833         cutoff = LONG_MAX / base;
 834         cutlim = LONG_MAX % base;
 835         
 836         for (i = Z_STRLEN_P(arg); i > 0; i--) {
 837                 c = *s++;
 838 
 839                 /* might not work for EBCDIC */
 840                 if (c >= '0' && c <= '9') 
 841                         c -= '0';
 842                 else if (c >= 'A' && c <= 'Z') 
 843                         c -= 'A' - 10;
 844                 else if (c >= 'a' && c <= 'z') 
 845                         c -= 'a' - 10;
 846                 else
 847                         continue;
 848 
 849                 if (c >= base)
 850                         continue;
 851                 
 852                 switch (mode) {
 853                 case 0: /* Integer */
 854                         if (num < cutoff || (num == cutoff && c <= cutlim)) {
 855                                 num = num * base + c;
 856                                 break;
 857                         } else {
 858                                 fnum = num;
 859                                 mode = 1;
 860                         }
 861                         /* fall-through */
 862                 case 1: /* Float */
 863                         fnum = fnum * base + c;
 864                 }       
 865         }
 866 
 867         if (mode == 1) {
 868                 ZVAL_DOUBLE(ret, fnum);
 869         } else {
 870                 ZVAL_LONG(ret, num);
 871         }
 872         return SUCCESS;
 873 }
 874 /* }}} */
 875 
 876 /* {{{ _php_math_longtobase */
 877 /*
 878  * Convert a long to a string containing a base(2-36) representation of
 879  * the number.
 880  */
 881 PHPAPI char * _php_math_longtobase(zval *arg, int base)
 882 {
 883         static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
 884         char buf[(sizeof(unsigned long) << 3) + 1];
 885         char *ptr, *end;
 886         unsigned long value;
 887 
 888         if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
 889                 return STR_EMPTY_ALLOC();
 890         }
 891 
 892         value = Z_LVAL_P(arg);
 893 
 894         end = ptr = buf + sizeof(buf) - 1;
 895         *ptr = '\0';
 896 
 897         do {
 898                 *--ptr = digits[value % base];
 899                 value /= base;
 900         } while (ptr > buf && value);
 901 
 902         return estrndup(ptr, end - ptr);
 903 }
 904 /* }}} */
 905 
 906 /* {{{ _php_math_zvaltobase */
 907 /*
 908  * Convert a zval to a string containing a base(2-36) representation of
 909  * the number.
 910  */
 911 PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
 912 {
 913         static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
 914 
 915         if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
 916                 return STR_EMPTY_ALLOC();
 917         }
 918 
 919         if (Z_TYPE_P(arg) == IS_DOUBLE) {
 920                 double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
 921                 char *ptr, *end;
 922                 char buf[(sizeof(double) << 3) + 1];
 923 
 924                 /* Don't try to convert +/- infinity */
 925                 if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
 926                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
 927                         return STR_EMPTY_ALLOC();
 928                 }
 929 
 930                 end = ptr = buf + sizeof(buf) - 1;
 931                 *ptr = '\0';
 932 
 933                 do {
 934                         *--ptr = digits[(int) fmod(fvalue, base)];
 935                         fvalue /= base;
 936                 } while (ptr > buf && fabs(fvalue) >= 1);
 937 
 938                 return estrndup(ptr, end - ptr);
 939         }
 940         
 941         return _php_math_longtobase(arg, base);
 942 }       
 943 /* }}} */
 944 
 945 /* {{{ proto int bindec(string binary_number)
 946    Returns the decimal equivalent of the binary number */
 947 PHP_FUNCTION(bindec)
 948 {
 949         zval **arg;
 950         
 951         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
 952                 return;
 953         }
 954         convert_to_string_ex(arg);
 955         if (_php_math_basetozval(*arg, 2, return_value) == FAILURE) {
 956                 RETURN_FALSE;
 957         }
 958 }
 959 /* }}} */
 960 
 961 /* {{{ proto int hexdec(string hexadecimal_number)
 962    Returns the decimal equivalent of the hexadecimal number */
 963 PHP_FUNCTION(hexdec)
 964 {
 965         zval **arg;
 966         
 967         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
 968                 return;
 969         }
 970         convert_to_string_ex(arg);
 971         if (_php_math_basetozval(*arg, 16, return_value) == FAILURE) {
 972                 RETURN_FALSE;
 973         }
 974 }
 975 /* }}} */
 976 
 977 /* {{{ proto int octdec(string octal_number)
 978    Returns the decimal equivalent of an octal string */
 979 PHP_FUNCTION(octdec)
 980 {
 981         zval **arg;
 982         
 983         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
 984                 return;
 985         }
 986         convert_to_string_ex(arg);
 987         if (_php_math_basetozval(*arg, 8, return_value) == FAILURE) {
 988                 RETURN_FALSE;
 989         }
 990 }
 991 /* }}} */
 992 
 993 /* {{{ proto string decbin(int decimal_number)
 994    Returns a string containing a binary representation of the number */
 995 PHP_FUNCTION(decbin)
 996 {
 997         zval **arg;
 998         char *result;
 999 
1000         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1001                 return;
1002         }
1003         convert_to_long_ex(arg);
1004         result = _php_math_longtobase(*arg, 2);
1005         RETURN_STRING(result, 0);
1006 }
1007 /* }}} */
1008 
1009 /* {{{ proto string decoct(int decimal_number)
1010    Returns a string containing an octal representation of the given number */
1011 PHP_FUNCTION(decoct)
1012 {
1013         zval **arg;
1014         char *result;
1015 
1016         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1017                 return;
1018         }
1019         convert_to_long_ex(arg);
1020         result = _php_math_longtobase(*arg, 8);
1021         RETURN_STRING(result, 0);
1022 }
1023 /* }}} */
1024 
1025 /* {{{ proto string dechex(int decimal_number)
1026    Returns a string containing a hexadecimal representation of the given number */
1027 PHP_FUNCTION(dechex)
1028 {
1029         zval **arg;
1030         char *result;
1031 
1032         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1033                 return;
1034         }
1035         convert_to_long_ex(arg);
1036         result = _php_math_longtobase(*arg, 16);
1037         RETURN_STRING(result, 0);
1038 }
1039 /* }}} */
1040 
1041 /* {{{ proto string base_convert(string number, int frombase, int tobase)
1042    Converts a number in a string from any base <= 36 to any base <= 36 */
1043 PHP_FUNCTION(base_convert)
1044 {
1045         zval **number, temp;
1046         long frombase, tobase;
1047         char *result;
1048 
1049         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zll", &number, &frombase, &tobase) == FAILURE) {
1050                 return;
1051         }
1052         convert_to_string_ex(number);
1053         
1054         if (frombase < 2 || frombase > 36) {
1055                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", frombase);
1056                 RETURN_FALSE;
1057         }
1058         if (tobase < 2 || tobase > 36) {
1059                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", tobase);
1060                 RETURN_FALSE;
1061         }
1062 
1063         if(_php_math_basetozval(*number, frombase, &temp) == FAILURE) {
1064                 RETURN_FALSE;
1065         }
1066         result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
1067         RETVAL_STRING(result, 0);
1068 } 
1069 /* }}} */
1070 
1071 /* {{{ _php_math_number_format 
1072 */
1073 PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1074 {
1075         return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1076 }
1077 
1078 static char *_php_math_number_format_ex_len(double d, int dec, char *dec_point,
1079                 size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len,
1080                 int *result_len)
1081 {
1082         char *tmpbuf = NULL, *resbuf;
1083         char *s, *t;  /* source, target */
1084         char *dp;
1085         int integral;
1086         int tmplen, reslen=0;
1087         int count=0;
1088         int is_negative=0;
1089 
1090         if (d < 0) {
1091                 is_negative = 1;
1092                 d = -d;
1093         }
1094 
1095         dec = MAX(0, dec);
1096         d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1097 
1098         tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d);
1099 
1100         if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
1101                 if (result_len) {
1102                         *result_len = tmplen;
1103                 }
1104 
1105                 return tmpbuf;
1106         }
1107 
1108         /* find decimal point, if expected */
1109         if (dec) {
1110                 dp = strpbrk(tmpbuf, ".,");
1111         } else {
1112                 dp = NULL;
1113         }
1114 
1115         /* calculate the length of the return buffer */
1116         if (dp) {
1117                 integral = dp - tmpbuf;
1118         } else {
1119                 /* no decimal point was found */
1120                 integral = tmplen;
1121         }
1122 
1123         /* allow for thousand separators */
1124         if (thousand_sep) {
1125                 integral += thousand_sep_len * ((integral-1) / 3);
1126         }
1127         
1128         reslen = integral;
1129         
1130         if (dec) {
1131                 reslen += dec;
1132 
1133                 if (dec_point) {
1134                         reslen += dec_point_len;
1135                 }
1136         }
1137 
1138         /* add a byte for minus sign */
1139         if (is_negative) {
1140                 reslen++;
1141         }
1142         resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */
1143 
1144         s = tmpbuf+tmplen-1;
1145         t = resbuf+reslen;
1146         *t-- = '\0';
1147 
1148         /* copy the decimal places.
1149          * Take care, as the sprintf implementation may return less places than
1150          * we requested due to internal buffer limitations */
1151         if (dec) {
1152                 int declen = dp ? s - dp : 0;
1153                 int topad = dec > declen ? dec - declen : 0;
1154 
1155                 /* pad with '0's */
1156                 while (topad--) {
1157                         *t-- = '0';
1158                 }
1159                 
1160                 if (dp) {
1161                         s -= declen + 1; /* +1 to skip the point */
1162                         t -= declen;
1163 
1164                         /* now copy the chars after the point */
1165                         memcpy(t + 1, dp + 1, declen);
1166                 }
1167 
1168                 /* add decimal point */
1169                 if (dec_point) {
1170                         t -= dec_point_len;
1171                         memcpy(t + 1, dec_point, dec_point_len);
1172                 }
1173         }
1174 
1175         /* copy the numbers before the decimal point, adding thousand
1176          * separator every three digits */
1177         while(s >= tmpbuf) {
1178                 *t-- = *s--;
1179                 if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
1180                         t -= thousand_sep_len;
1181                         memcpy(t + 1, thousand_sep, thousand_sep_len);
1182                 }
1183         }
1184 
1185         /* and a minus sign, if needed */
1186         if (is_negative) {
1187                 *t-- = '-';
1188         }
1189 
1190         efree(tmpbuf);
1191         
1192         if (result_len) {
1193                 *result_len = reslen;
1194         }
1195 
1196         return resbuf;
1197 }
1198 
1199 PHPAPI char *_php_math_number_format_ex(double d, int dec, char *dec_point,
1200                 size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len)
1201 {
1202         return _php_math_number_format_ex_len(d, dec, dec_point, dec_point_len,
1203                         thousand_sep, thousand_sep_len, NULL);
1204 }
1205 /* }}} */
1206 
1207 /* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_separator, string thousands_separator]])
1208    Formats a number with grouped thousands */
1209 PHP_FUNCTION(number_format)
1210 {
1211         double num;
1212         long dec = 0;
1213         char *thousand_sep = NULL, *dec_point = NULL;
1214         char thousand_sep_chr = ',', dec_point_chr = '.';
1215         int thousand_sep_len = 0, dec_point_len = 0;
1216         
1217         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) {
1218                 return;
1219         }
1220 
1221         switch(ZEND_NUM_ARGS()) {
1222         case 1:
1223                 RETURN_STRING(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr), 0);
1224                 break;
1225         case 2:
1226                 RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1227                 break;
1228         case 4:
1229                 if (dec_point == NULL) {
1230                         dec_point = &dec_point_chr;
1231                         dec_point_len = 1;
1232                 }
1233 
1234                 if (thousand_sep == NULL) {
1235                         thousand_sep = &thousand_sep_chr;
1236                         thousand_sep_len = 1;
1237                 }
1238 
1239                 Z_TYPE_P(return_value) = IS_STRING;
1240                 Z_STRVAL_P(return_value) = _php_math_number_format_ex_len(num, dec,
1241                                 dec_point, dec_point_len, thousand_sep, thousand_sep_len,
1242                                 &Z_STRLEN_P(return_value));
1243                 break;
1244         default:
1245                 WRONG_PARAM_COUNT;
1246                 break;
1247         }
1248 }
1249 /* }}} */
1250 
1251 /* {{{ proto float fmod(float x, float y)
1252    Returns the remainder of dividing x by y as a float */
1253 PHP_FUNCTION(fmod)
1254 {
1255         double num1, num2;
1256 
1257         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd",  &num1, &num2) == FAILURE) {
1258                 return;
1259         }
1260         RETURN_DOUBLE(fmod(num1, num2));
1261 }
1262 /* }}} */
1263 
1264 
1265 
1266 /*
1267  * Local variables:
1268  * tab-width: 4
1269  * c-basic-offset: 4
1270  * End:
1271  * vim600: fdm=marker
1272  * vim: noet sw=4 ts=4
1273  */

/* [<][>][^][v][top][bottom][index][help] */