123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329 |
- /*
- +----------------------------------------------------------------------+
- | PHP Version 7 |
- +----------------------------------------------------------------------+
- | Copyright (c) 1997-2018 The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Jim Winstead <jimw@php.net> |
- | Stig Sæther Bakken <ssb@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net> |
- +----------------------------------------------------------------------+
- */
- #include "php.h"
- #include "php_math.h"
- #include "zend_multiply.h"
- #include "zend_exceptions.h"
- #include "zend_portability.h"
- #include <math.h>
- #include <float.h>
- #include <stdlib.h>
- #include "basic_functions.h"
- /* {{{ php_intlog10abs
- Returns floor(log10(fabs(val))), uses fast binary search */
- static inline int php_intlog10abs(double value) {
- int result;
- value = fabs(value);
- if (value < 1e-8 || value > 1e22) {
- result = (int)floor(log10(value));
- } else {
- static const double values[] = {
- 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
- 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
- 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
- 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
- /* Do a binary search with 5 steps */
- result = 15;
- if (value < values[result]) {
- result -= 8;
- } else {
- result += 8;
- }
- if (value < values[result]) {
- result -= 4;
- } else {
- result += 4;
- }
- if (value < values[result]) {
- result -= 2;
- } else {
- result += 2;
- }
- if (value < values[result]) {
- result -= 1;
- } else {
- result += 1;
- }
- if (value < values[result]) {
- result -= 1;
- }
- result -= 8;
- }
- return result;
- }
- /* }}} */
- /* {{{ php_intpow10
- Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
- static inline double php_intpow10(int power) {
- static const double powers[] = {
- 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
- 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
- 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
- /* Not in lookup table */
- if (power < 0 || power > 22) {
- return pow(10.0, (double)power);
- }
- return powers[power];
- }
- /* }}} */
- /* {{{ php_round_helper
- Actually performs the rounding of a value to integer in a certain mode */
- static inline double php_round_helper(double value, int mode) {
- double tmp_value;
- if (value >= 0.0) {
- tmp_value = floor(value + 0.5);
- if ((mode == PHP_ROUND_HALF_DOWN && value == (-0.5 + tmp_value)) ||
- (mode == PHP_ROUND_HALF_EVEN && value == (0.5 + 2 * floor(tmp_value/2.0))) ||
- (mode == PHP_ROUND_HALF_ODD && value == (0.5 + 2 * floor(tmp_value/2.0) - 1.0)))
- {
- tmp_value = tmp_value - 1.0;
- }
- } else {
- tmp_value = ceil(value - 0.5);
- if ((mode == PHP_ROUND_HALF_DOWN && value == (0.5 + tmp_value)) ||
- (mode == PHP_ROUND_HALF_EVEN && value == (-0.5 + 2 * ceil(tmp_value/2.0))) ||
- (mode == PHP_ROUND_HALF_ODD && value == (-0.5 + 2 * ceil(tmp_value/2.0) + 1.0)))
- {
- tmp_value = tmp_value + 1.0;
- }
- }
- return tmp_value;
- }
- /* }}} */
- /* {{{ _php_math_round */
- /*
- * Rounds a number to a certain number of decimal places in a certain rounding
- * mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding
- */
- PHPAPI double _php_math_round(double value, int places, int mode) {
- double f1, f2;
- double tmp_value;
- int precision_places;
- if (!zend_finite(value)) {
- return value;
- }
- places = places < INT_MIN+1 ? INT_MIN+1 : places;
- precision_places = 14 - php_intlog10abs(value);
- f1 = php_intpow10(abs(places));
- /* If the decimal precision guaranteed by FP arithmetic is higher than
- the requested places BUT is small enough to make sure a non-zero value
- is returned, pre-round the result to the precision */
- if (precision_places > places && precision_places - places < 15) {
- int64_t use_precision = precision_places < INT_MIN+1 ? INT_MIN+1 : precision_places;
- f2 = php_intpow10(abs((int)use_precision));
- if (use_precision >= 0) {
- tmp_value = value * f2;
- } else {
- tmp_value = value / f2;
- }
- /* preround the result (tmp_value will always be something * 1e14,
- thus never larger than 1e15 here) */
- tmp_value = php_round_helper(tmp_value, mode);
- use_precision = places - precision_places;
- use_precision = use_precision < INT_MIN+1 ? INT_MIN+1 : use_precision;
- /* now correctly move the decimal point */
- f2 = php_intpow10(abs((int)use_precision));
- /* because places < precision_places */
- tmp_value = tmp_value / f2;
- } else {
- /* adjust the value */
- if (places >= 0) {
- tmp_value = value * f1;
- } else {
- tmp_value = value / f1;
- }
- /* This value is beyond our precision, so rounding it is pointless */
- if (fabs(tmp_value) >= 1e15) {
- return value;
- }
- }
- /* round the temp value */
- tmp_value = php_round_helper(tmp_value, mode);
- /* see if it makes sense to use simple division to round the value */
- if (abs(places) < 23) {
- if (places > 0) {
- tmp_value = tmp_value / f1;
- } else {
- tmp_value = tmp_value * f1;
- }
- } else {
- /* Simple division can't be used since that will cause wrong results.
- Instead, the number is converted to a string and back again using
- strtod(). strtod() will return the nearest possible FP value for
- that string. */
- /* 40 Bytes should be more than enough for this format string. The
- float won't be larger than 1e15 anyway. But just in case, use
- snprintf() and make sure the buffer is zero-terminated */
- char buf[40];
- snprintf(buf, 39, "%15fe%d", tmp_value, -places);
- buf[39] = '\0';
- tmp_value = zend_strtod(buf, NULL);
- /* couldn't convert to string and back */
- if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
- tmp_value = value;
- }
- }
- return tmp_value;
- }
- /* }}} */
- /* {{{ php_asinh
- */
- static double php_asinh(double z)
- {
- #ifdef HAVE_ASINH
- return(asinh(z));
- #else
- # ifdef _WIN64
- if (z >= 0) {
- return log(z + sqrt(z * z + 1));
- }
- else {
- return -log(-z + sqrt(z * z + 1));
- }
- # else
- return(log(z + sqrt(1 + pow(z, 2))) / log(M_E));
- # endif
- #endif
- }
- /* }}} */
- /* {{{ php_acosh
- */
- static double php_acosh(double x)
- {
- #ifdef HAVE_ACOSH
- return(acosh(x));
- #else
- # ifdef _WIN64
- if (x >= 1) {
- return log(x + sqrt(x * x - 1));
- } else {
- return ZEND_NAN;
- }
- # else
- return(log(x + sqrt(x * x - 1)));
- # endif
- #endif
- }
- /* }}} */
- /* {{{ php_atanh
- */
- static double php_atanh(double z)
- {
- #ifdef HAVE_ATANH
- return(atanh(z));
- #else
- return(0.5 * log((1 + z) / (1 - z)));
- #endif
- }
- /* }}} */
- /* {{{ php_log1p
- */
- static double php_log1p(double x)
- {
- #ifdef HAVE_LOG1P
- return(log1p(x));
- #else
- return(log(1 + x));
- #endif
- }
- /* }}} */
- /* {{{ php_expm1
- */
- static double php_expm1(double x)
- {
- #ifndef PHP_WIN32
- return(expm1(x));
- #else
- return(exp(x) - 1);
- #endif
- }
- /* }}}*/
- /* {{{ proto int abs(int number)
- Return the absolute value of the number */
- PHP_FUNCTION(abs)
- {
- zval *value;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(value)
- ZEND_PARSE_PARAMETERS_END();
- convert_scalar_to_number_ex(value);
- if (Z_TYPE_P(value) == IS_DOUBLE) {
- RETURN_DOUBLE(fabs(Z_DVAL_P(value)));
- } else if (Z_TYPE_P(value) == IS_LONG) {
- if (Z_LVAL_P(value) == ZEND_LONG_MIN) {
- RETURN_DOUBLE(-(double)ZEND_LONG_MIN);
- } else {
- RETURN_LONG(Z_LVAL_P(value) < 0 ? -Z_LVAL_P(value) : Z_LVAL_P(value));
- }
- }
- RETURN_FALSE;
- }
- /* }}} */
- /* {{{ proto float ceil(float number)
- Returns the next highest integer value of the number */
- PHP_FUNCTION(ceil)
- {
- zval *value;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(value)
- ZEND_PARSE_PARAMETERS_END();
- convert_scalar_to_number_ex(value);
- if (Z_TYPE_P(value) == IS_DOUBLE) {
- RETURN_DOUBLE(ceil(Z_DVAL_P(value)));
- } else if (Z_TYPE_P(value) == IS_LONG) {
- RETURN_DOUBLE(zval_get_double(value));
- }
- RETURN_FALSE;
- }
- /* }}} */
- /* {{{ proto float floor(float number)
- Returns the next lowest integer value from the number */
- PHP_FUNCTION(floor)
- {
- zval *value;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(value)
- ZEND_PARSE_PARAMETERS_END();
- convert_scalar_to_number_ex(value);
- if (Z_TYPE_P(value) == IS_DOUBLE) {
- RETURN_DOUBLE(floor(Z_DVAL_P(value)));
- } else if (Z_TYPE_P(value) == IS_LONG) {
- RETURN_DOUBLE(zval_get_double(value));
- }
- RETURN_FALSE;
- }
- /* }}} */
- /* {{{ proto float round(float number [, int precision [, int mode]])
- Returns the number rounded to specified precision */
- PHP_FUNCTION(round)
- {
- zval *value;
- int places = 0;
- zend_long precision = 0;
- zend_long mode = PHP_ROUND_HALF_UP;
- double return_val;
- ZEND_PARSE_PARAMETERS_START(1, 3)
- Z_PARAM_ZVAL(value)
- Z_PARAM_OPTIONAL
- Z_PARAM_LONG(precision)
- Z_PARAM_LONG(mode)
- ZEND_PARSE_PARAMETERS_END();
- if (ZEND_NUM_ARGS() >= 2) {
- #if SIZEOF_ZEND_LONG > SIZEOF_INT
- if (precision >= 0) {
- places = precision > INT_MAX ? INT_MAX : (int)precision;
- } else {
- places = precision <= INT_MIN ? INT_MIN+1 : (int)precision;
- }
- #else
- places = precision;
- #endif
- }
- convert_scalar_to_number_ex(value);
- switch (Z_TYPE_P(value)) {
- case IS_LONG:
- /* Simple case - long that doesn't need to be rounded. */
- if (places >= 0) {
- RETURN_DOUBLE((double) Z_LVAL_P(value));
- }
- /* break omitted intentionally */
- case IS_DOUBLE:
- return_val = (Z_TYPE_P(value) == IS_LONG) ? (double)Z_LVAL_P(value) : Z_DVAL_P(value);
- return_val = _php_math_round(return_val, (int)places, (int)mode);
- RETURN_DOUBLE(return_val);
- break;
- default:
- RETURN_FALSE;
- break;
- }
- }
- /* }}} */
- /* {{{ proto float sin(float number)
- Returns the sine of the number in radians */
- PHP_FUNCTION(sin)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(sin(num));
- }
- /* }}} */
- /* {{{ proto float cos(float number)
- Returns the cosine of the number in radians */
- PHP_FUNCTION(cos)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(cos(num));
- }
- /* }}} */
- /* {{{ proto float tan(float number)
- Returns the tangent of the number in radians */
- PHP_FUNCTION(tan)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(tan(num));
- }
- /* }}} */
- /* {{{ proto float asin(float number)
- Returns the arc sine of the number in radians */
- PHP_FUNCTION(asin)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(asin(num));
- }
- /* }}} */
- /* {{{ proto float acos(float number)
- Return the arc cosine of the number in radians */
- PHP_FUNCTION(acos)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(acos(num));
- }
- /* }}} */
- /* {{{ proto float atan(float number)
- Returns the arc tangent of the number in radians */
- PHP_FUNCTION(atan)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(atan(num));
- }
- /* }}} */
- /* {{{ proto float atan2(float y, float x)
- Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
- PHP_FUNCTION(atan2)
- {
- double num1, num2;
- ZEND_PARSE_PARAMETERS_START(2, 2)
- Z_PARAM_DOUBLE(num1)
- Z_PARAM_DOUBLE(num2)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(atan2(num1, num2));
- }
- /* }}} */
- /* {{{ proto float sinh(float number)
- Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
- PHP_FUNCTION(sinh)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(sinh(num));
- }
- /* }}} */
- /* {{{ proto float cosh(float number)
- Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
- PHP_FUNCTION(cosh)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(cosh(num));
- }
- /* }}} */
- /* {{{ proto float tanh(float number)
- Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
- PHP_FUNCTION(tanh)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(tanh(num));
- }
- /* }}} */
- /* {{{ proto float asinh(float number)
- Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
- PHP_FUNCTION(asinh)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(php_asinh(num));
- }
- /* }}} */
- /* {{{ proto float acosh(float number)
- Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
- PHP_FUNCTION(acosh)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(php_acosh(num));
- }
- /* }}} */
- /* {{{ proto float atanh(float number)
- Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
- PHP_FUNCTION(atanh)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(php_atanh(num));
- }
- /* }}} */
- /* {{{ proto float pi(void)
- Returns an approximation of pi */
- PHP_FUNCTION(pi)
- {
- RETURN_DOUBLE(M_PI);
- }
- /* }}} */
- /* {{{ proto bool is_finite(float val)
- Returns whether argument is finite */
- PHP_FUNCTION(is_finite)
- {
- double dval;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(dval)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_BOOL(zend_finite(dval));
- }
- /* }}} */
- /* {{{ proto bool is_infinite(float val)
- Returns whether argument is infinite */
- PHP_FUNCTION(is_infinite)
- {
- double dval;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(dval)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_BOOL(zend_isinf(dval));
- }
- /* }}} */
- /* {{{ proto bool is_nan(float val)
- Returns whether argument is not a number */
- PHP_FUNCTION(is_nan)
- {
- double dval;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(dval)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_BOOL(zend_isnan(dval));
- }
- /* }}} */
- /* {{{ proto number pow(number base, number exponent)
- Returns base raised to the power of exponent. Returns integer result when possible */
- PHP_FUNCTION(pow)
- {
- zval *zbase, *zexp;
- ZEND_PARSE_PARAMETERS_START(2, 2)
- Z_PARAM_ZVAL(zbase)
- Z_PARAM_ZVAL(zexp)
- ZEND_PARSE_PARAMETERS_END();
- pow_function(return_value, zbase, zexp);
- }
- /* }}} */
- /* {{{ proto float exp(float number)
- Returns e raised to the power of the number */
- PHP_FUNCTION(exp)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(exp(num));
- }
- /* }}} */
- /* {{{ proto float expm1(float number)
- Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
- /*
- WARNING: this function is expermental: it could change its name or
- disappear in the next version of PHP!
- */
- PHP_FUNCTION(expm1)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(php_expm1(num));
- }
- /* }}} */
- /* {{{ proto float log1p(float number)
- Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
- /*
- WARNING: this function is expermental: it could change its name or
- disappear in the next version of PHP!
- */
- PHP_FUNCTION(log1p)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(php_log1p(num));
- }
- /* }}} */
- /* {{{ proto float log(float number, [float base])
- Returns the natural logarithm of the number, or the base log if base is specified */
- PHP_FUNCTION(log)
- {
- double num, base = 0;
- ZEND_PARSE_PARAMETERS_START(1, 2)
- Z_PARAM_DOUBLE(num)
- Z_PARAM_OPTIONAL
- Z_PARAM_DOUBLE(base)
- ZEND_PARSE_PARAMETERS_END();
- if (ZEND_NUM_ARGS() == 1) {
- RETURN_DOUBLE(log(num));
- }
- #ifdef HAVE_LOG2
- if (base == 2.0) {
- RETURN_DOUBLE(log2(num));
- }
- #endif
- if (base == 10.0) {
- RETURN_DOUBLE(log10(num));
- }
- if (base == 1.0) {
- RETURN_DOUBLE(ZEND_NAN);
- }
- if (base <= 0.0) {
- php_error_docref(NULL, E_WARNING, "base must be greater than 0");
- RETURN_FALSE;
- }
- RETURN_DOUBLE(log(num) / log(base));
- }
- /* }}} */
- /* {{{ proto float log10(float number)
- Returns the base-10 logarithm of the number */
- PHP_FUNCTION(log10)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(log10(num));
- }
- /* }}} */
- /* {{{ proto float sqrt(float number)
- Returns the square root of the number */
- PHP_FUNCTION(sqrt)
- {
- double num;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(num)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(sqrt(num));
- }
- /* }}} */
- /* {{{ proto float hypot(float num1, float num2)
- Returns sqrt(num1*num1 + num2*num2) */
- PHP_FUNCTION(hypot)
- {
- double num1, num2;
- ZEND_PARSE_PARAMETERS_START(2, 2)
- Z_PARAM_DOUBLE(num1)
- Z_PARAM_DOUBLE(num2)
- ZEND_PARSE_PARAMETERS_END();
- #if HAVE_HYPOT
- RETURN_DOUBLE(hypot(num1, num2));
- #elif defined(_MSC_VER)
- RETURN_DOUBLE(_hypot(num1, num2));
- #else
- RETURN_DOUBLE(sqrt((num1 * num1) + (num2 * num2)));
- #endif
- }
- /* }}} */
- /* {{{ proto float deg2rad(float number)
- Converts the number in degrees to the radian equivalent */
- PHP_FUNCTION(deg2rad)
- {
- double deg;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(deg)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE((deg / 180.0) * M_PI);
- }
- /* }}} */
- /* {{{ proto float rad2deg(float number)
- Converts the radian number to the equivalent number in degrees */
- PHP_FUNCTION(rad2deg)
- {
- double rad;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_DOUBLE(rad)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE((rad / M_PI) * 180);
- }
- /* }}} */
- /* {{{ _php_math_basetolong */
- /*
- * Convert a string representation of a base(2-36) number to a long.
- */
- PHPAPI zend_long _php_math_basetolong(zval *arg, int base)
- {
- zend_long num = 0, digit, onum;
- zend_long i;
- char c, *s;
- if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
- return 0;
- }
- s = Z_STRVAL_P(arg);
- for (i = Z_STRLEN_P(arg); i > 0; i--) {
- c = *s++;
- digit = (c >= '0' && c <= '9') ? c - '0'
- : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
- : (c >= 'a' && c <= 'z') ? c - 'a' + 10
- : base;
- if (digit >= base) {
- continue;
- }
- onum = num;
- num = num * base + digit;
- if (num > onum)
- continue;
- {
- php_error_docref(NULL, E_WARNING, "Number '%s' is too big to fit in long", s);
- return ZEND_LONG_MAX;
- }
- }
- return num;
- }
- /* }}} */
- /* {{{ _php_math_basetozval */
- /*
- * Convert a string representation of a base(2-36) number to a zval.
- */
- PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
- {
- zend_long num = 0;
- double fnum = 0;
- zend_long i;
- int mode = 0;
- char c, *s;
- zend_long cutoff;
- int cutlim;
- if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
- return FAILURE;
- }
- s = Z_STRVAL_P(arg);
- cutoff = ZEND_LONG_MAX / base;
- cutlim = ZEND_LONG_MAX % base;
- for (i = Z_STRLEN_P(arg); i > 0; i--) {
- c = *s++;
- /* might not work for EBCDIC */
- if (c >= '0' && c <= '9')
- c -= '0';
- else if (c >= 'A' && c <= 'Z')
- c -= 'A' - 10;
- else if (c >= 'a' && c <= 'z')
- c -= 'a' - 10;
- else
- continue;
- if (c >= base)
- continue;
- switch (mode) {
- case 0: /* Integer */
- if (num < cutoff || (num == cutoff && c <= cutlim)) {
- num = num * base + c;
- break;
- } else {
- fnum = (double)num;
- mode = 1;
- }
- /* fall-through */
- case 1: /* Float */
- fnum = fnum * base + c;
- }
- }
- if (mode == 1) {
- ZVAL_DOUBLE(ret, fnum);
- } else {
- ZVAL_LONG(ret, num);
- }
- return SUCCESS;
- }
- /* }}} */
- /* {{{ _php_math_longtobase */
- /*
- * Convert a long to a string containing a base(2-36) representation of
- * the number.
- */
- PHPAPI zend_string * _php_math_longtobase(zval *arg, int base)
- {
- static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
- char buf[(sizeof(zend_ulong) << 3) + 1];
- char *ptr, *end;
- zend_ulong value;
- if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
- return ZSTR_EMPTY_ALLOC();
- }
- value = Z_LVAL_P(arg);
- end = ptr = buf + sizeof(buf) - 1;
- *ptr = '\0';
- do {
- ZEND_ASSERT(ptr > buf);
- *--ptr = digits[value % base];
- value /= base;
- } while (value);
- return zend_string_init(ptr, end - ptr, 0);
- }
- /* }}} */
- /* {{{ _php_math_zvaltobase */
- /*
- * Convert a zval to a string containing a base(2-36) representation of
- * the number.
- */
- PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base)
- {
- static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
- if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
- return ZSTR_EMPTY_ALLOC();
- }
- if (Z_TYPE_P(arg) == IS_DOUBLE) {
- double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
- char *ptr, *end;
- char buf[(sizeof(double) << 3) + 1];
- /* Don't try to convert +/- infinity */
- if (fvalue == ZEND_INFINITY || fvalue == -ZEND_INFINITY) {
- php_error_docref(NULL, E_WARNING, "Number too large");
- return ZSTR_EMPTY_ALLOC();
- }
- end = ptr = buf + sizeof(buf) - 1;
- *ptr = '\0';
- do {
- *--ptr = digits[(int) fmod(fvalue, base)];
- fvalue /= base;
- } while (ptr > buf && fabs(fvalue) >= 1);
- return zend_string_init(ptr, end - ptr, 0);
- }
- return _php_math_longtobase(arg, base);
- }
- /* }}} */
- /* {{{ proto int bindec(string binary_number)
- Returns the decimal equivalent of the binary number */
- PHP_FUNCTION(bindec)
- {
- zval *arg;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(arg)
- ZEND_PARSE_PARAMETERS_END();
- convert_to_string_ex(arg);
- if (_php_math_basetozval(arg, 2, return_value) == FAILURE) {
- RETURN_FALSE;
- }
- }
- /* }}} */
- /* {{{ proto int hexdec(string hexadecimal_number)
- Returns the decimal equivalent of the hexadecimal number */
- PHP_FUNCTION(hexdec)
- {
- zval *arg;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(arg)
- ZEND_PARSE_PARAMETERS_END();
- convert_to_string_ex(arg);
- if (_php_math_basetozval(arg, 16, return_value) == FAILURE) {
- RETURN_FALSE;
- }
- }
- /* }}} */
- /* {{{ proto int octdec(string octal_number)
- Returns the decimal equivalent of an octal string */
- PHP_FUNCTION(octdec)
- {
- zval *arg;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(arg)
- ZEND_PARSE_PARAMETERS_END();
- convert_to_string_ex(arg);
- if (_php_math_basetozval(arg, 8, return_value) == FAILURE) {
- RETURN_FALSE;
- }
- }
- /* }}} */
- /* {{{ proto string decbin(int decimal_number)
- Returns a string containing a binary representation of the number */
- PHP_FUNCTION(decbin)
- {
- zval *arg;
- zend_string *result;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(arg)
- ZEND_PARSE_PARAMETERS_END();
- convert_to_long_ex(arg);
- result = _php_math_longtobase(arg, 2);
- RETURN_STR(result);
- }
- /* }}} */
- /* {{{ proto string decoct(int decimal_number)
- Returns a string containing an octal representation of the given number */
- PHP_FUNCTION(decoct)
- {
- zval *arg;
- zend_string *result;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(arg)
- ZEND_PARSE_PARAMETERS_END();
- convert_to_long_ex(arg);
- result = _php_math_longtobase(arg, 8);
- RETURN_STR(result);
- }
- /* }}} */
- /* {{{ proto string dechex(int decimal_number)
- Returns a string containing a hexadecimal representation of the given number */
- PHP_FUNCTION(dechex)
- {
- zval *arg;
- zend_string *result;
- ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ZVAL(arg)
- ZEND_PARSE_PARAMETERS_END();
- convert_to_long_ex(arg);
- result = _php_math_longtobase(arg, 16);
- RETURN_STR(result);
- }
- /* }}} */
- /* {{{ proto string base_convert(string number, int frombase, int tobase)
- Converts a number in a string from any base <= 36 to any base <= 36 */
- PHP_FUNCTION(base_convert)
- {
- zval *number, temp;
- zend_long frombase, tobase;
- zend_string *result;
- ZEND_PARSE_PARAMETERS_START(3, 3)
- Z_PARAM_ZVAL(number)
- Z_PARAM_LONG(frombase)
- Z_PARAM_LONG(tobase)
- ZEND_PARSE_PARAMETERS_END();
- convert_to_string_ex(number);
- if (frombase < 2 || frombase > 36) {
- php_error_docref(NULL, E_WARNING, "Invalid `from base' (" ZEND_LONG_FMT ")", frombase);
- RETURN_FALSE;
- }
- if (tobase < 2 || tobase > 36) {
- php_error_docref(NULL, E_WARNING, "Invalid `to base' (" ZEND_LONG_FMT ")", tobase);
- RETURN_FALSE;
- }
- if(_php_math_basetozval(number, (int)frombase, &temp) == FAILURE) {
- RETURN_FALSE;
- }
- result = _php_math_zvaltobase(&temp, (int)tobase);
- RETVAL_STR(result);
- }
- /* }}} */
- /* {{{ _php_math_number_format
- */
- PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
- {
- return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
- }
- PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, char *dec_point,
- size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len)
- {
- zend_string *res;
- zend_string *tmpbuf;
- char *s, *t; /* source, target */
- char *dp;
- size_t integral;
- size_t reslen = 0;
- int count = 0;
- int is_negative=0;
- if (d < 0) {
- is_negative = 1;
- d = -d;
- }
- dec = MAX(0, dec);
- d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
- tmpbuf = strpprintf(0, "%.*F", dec, d);
- if (tmpbuf == NULL) {
- return NULL;
- } else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) {
- return tmpbuf;
- }
- /* Check if the number is no longer negative after rounding */
- if (is_negative && d == 0) {
- is_negative = 0;
- }
- /* find decimal point, if expected */
- if (dec) {
- dp = strpbrk(ZSTR_VAL(tmpbuf), ".,");
- } else {
- dp = NULL;
- }
- /* calculate the length of the return buffer */
- if (dp) {
- integral = (dp - ZSTR_VAL(tmpbuf));
- } else {
- /* no decimal point was found */
- integral = ZSTR_LEN(tmpbuf);
- }
- /* allow for thousand separators */
- if (thousand_sep) {
- integral = zend_safe_addmult((integral-1)/3, thousand_sep_len, integral, "number formatting");
- }
- reslen = integral;
- if (dec) {
- reslen += dec;
- if (dec_point) {
- reslen = zend_safe_addmult(reslen, 1, dec_point_len, "number formatting");
- }
- }
- /* add a byte for minus sign */
- if (is_negative) {
- reslen++;
- }
- res = zend_string_alloc(reslen, 0);
- s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
- t = ZSTR_VAL(res) + reslen;
- *t-- = '\0';
- /* copy the decimal places.
- * Take care, as the sprintf implementation may return less places than
- * we requested due to internal buffer limitations */
- if (dec) {
- size_t declen = (dp ? s - dp : 0);
- size_t topad = (size_t)dec > declen ? dec - declen : 0;
- /* pad with '0's */
- while (topad--) {
- *t-- = '0';
- }
- if (dp) {
- s -= declen + 1; /* +1 to skip the point */
- t -= declen;
- /* now copy the chars after the point */
- memcpy(t + 1, dp + 1, declen);
- }
- /* add decimal point */
- if (dec_point) {
- t -= dec_point_len;
- memcpy(t + 1, dec_point, dec_point_len);
- }
- }
- /* copy the numbers before the decimal point, adding thousand
- * separator every three digits */
- while (s >= ZSTR_VAL(tmpbuf)) {
- *t-- = *s--;
- if (thousand_sep && (++count%3)==0 && s >= ZSTR_VAL(tmpbuf)) {
- t -= thousand_sep_len;
- memcpy(t + 1, thousand_sep, thousand_sep_len);
- }
- }
- /* and a minus sign, if needed */
- if (is_negative) {
- *t-- = '-';
- }
- ZSTR_LEN(res) = reslen;
- zend_string_release_ex(tmpbuf, 0);
- return res;
- }
- /* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_separator, string thousands_separator]])
- Formats a number with grouped thousands */
- PHP_FUNCTION(number_format)
- {
- double num;
- zend_long dec = 0;
- char *thousand_sep = NULL, *dec_point = NULL;
- char thousand_sep_chr = ',', dec_point_chr = '.';
- size_t thousand_sep_len = 0, dec_point_len = 0;
- ZEND_PARSE_PARAMETERS_START(1, 4)
- Z_PARAM_DOUBLE(num)
- Z_PARAM_OPTIONAL
- Z_PARAM_LONG(dec)
- Z_PARAM_STRING_EX(dec_point, dec_point_len, 1, 0)
- Z_PARAM_STRING_EX(thousand_sep, thousand_sep_len, 1, 0)
- ZEND_PARSE_PARAMETERS_END();
- switch(ZEND_NUM_ARGS()) {
- case 1:
- RETURN_STR(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr));
- break;
- case 2:
- RETURN_STR(_php_math_number_format(num, (int)dec, dec_point_chr, thousand_sep_chr));
- break;
- case 4:
- if (dec_point == NULL) {
- dec_point = &dec_point_chr;
- dec_point_len = 1;
- }
- if (thousand_sep == NULL) {
- thousand_sep = &thousand_sep_chr;
- thousand_sep_len = 1;
- }
- RETVAL_STR(_php_math_number_format_ex(num, (int)dec,
- dec_point, dec_point_len, thousand_sep, thousand_sep_len));
- break;
- default:
- WRONG_PARAM_COUNT;
- }
- }
- /* }}} */
- /* {{{ proto float fmod(float x, float y)
- Returns the remainder of dividing x by y as a float */
- PHP_FUNCTION(fmod)
- {
- double num1, num2;
- ZEND_PARSE_PARAMETERS_START(2, 2)
- Z_PARAM_DOUBLE(num1)
- Z_PARAM_DOUBLE(num2)
- ZEND_PARSE_PARAMETERS_END();
- RETURN_DOUBLE(fmod(num1, num2));
- }
- /* }}} */
- /* {{{ proto int intdiv(int dividend, int divisor)
- Returns the integer quotient of the division of dividend by divisor */
- PHP_FUNCTION(intdiv)
- {
- zend_long dividend, divisor;
- ZEND_PARSE_PARAMETERS_START(2, 2)
- Z_PARAM_LONG(dividend)
- Z_PARAM_LONG(divisor)
- ZEND_PARSE_PARAMETERS_END();
- if (divisor == 0) {
- zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
- return;
- } else if (divisor == -1 && dividend == ZEND_LONG_MIN) {
- /* Prevent overflow error/crash ... really should not happen:
- We don't return a float here as that violates function contract */
- zend_throw_exception_ex(zend_ce_arithmetic_error, 0, "Division of PHP_INT_MIN by -1 is not an integer");
- return;
- }
- RETURN_LONG(dividend / divisor);
- }
- /* }}} */
- /*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: fdm=marker
- * vim: noet sw=4 ts=4
- */
|