Return to math.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / standard |
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: */