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