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