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