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