1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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)) {
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 - places < 15) {
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 zend_long i;
854 int mode = 0;
855 char c, *s;
856 zend_long cutoff;
857 int cutlim;
858
859 if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
860 return FAILURE;
861 }
862
863 s = Z_STRVAL_P(arg);
864
865 cutoff = ZEND_LONG_MAX / base;
866 cutlim = ZEND_LONG_MAX % base;
867
868 for (i = Z_STRLEN_P(arg); i > 0; i--) {
869 c = *s++;
870
871 /* might not work for EBCDIC */
872 if (c >= '0' && c <= '9')
873 c -= '0';
874 else if (c >= 'A' && c <= 'Z')
875 c -= 'A' - 10;
876 else if (c >= 'a' && c <= 'z')
877 c -= 'a' - 10;
878 else
879 continue;
880
881 if (c >= base)
882 continue;
883
884 switch (mode) {
885 case 0: /* Integer */
886 if (num < cutoff || (num == cutoff && c <= cutlim)) {
887 num = num * base + c;
888 break;
889 } else {
890 fnum = (double)num;
891 mode = 1;
892 }
893 /* fall-through */
894 case 1: /* Float */
895 fnum = fnum * base + c;
896 }
897 }
898
899 if (mode == 1) {
900 ZVAL_DOUBLE(ret, fnum);
901 } else {
902 ZVAL_LONG(ret, num);
903 }
904 return SUCCESS;
905 }
906 /* }}} */
907
908 /* {{{ _php_math_longtobase */
909 /*
910 * Convert a long to a string containing a base(2-36) representation of
911 * the number.
912 */
_php_math_longtobase(zval * arg,int base)913 PHPAPI zend_string * _php_math_longtobase(zval *arg, int base)
914 {
915 static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
916 char buf[(sizeof(zend_ulong) << 3) + 1];
917 char *ptr, *end;
918 zend_ulong value;
919
920 if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
921 return ZSTR_EMPTY_ALLOC();
922 }
923
924 value = Z_LVAL_P(arg);
925
926 end = ptr = buf + sizeof(buf) - 1;
927 *ptr = '\0';
928
929 do {
930 ZEND_ASSERT(ptr > buf);
931 *--ptr = digits[value % base];
932 value /= base;
933 } while (value);
934
935 return zend_string_init(ptr, end - ptr, 0);
936 }
937 /* }}} */
938
939 /* {{{ _php_math_zvaltobase */
940 /*
941 * Convert a zval to a string containing a base(2-36) representation of
942 * the number.
943 */
_php_math_zvaltobase(zval * arg,int base)944 PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base)
945 {
946 static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
947
948 if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
949 return ZSTR_EMPTY_ALLOC();
950 }
951
952 if (Z_TYPE_P(arg) == IS_DOUBLE) {
953 double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
954 char *ptr, *end;
955 char buf[(sizeof(double) << 3) + 1];
956
957 /* Don't try to convert +/- infinity */
958 if (fvalue == ZEND_INFINITY || fvalue == -ZEND_INFINITY) {
959 php_error_docref(NULL, E_WARNING, "Number too large");
960 return ZSTR_EMPTY_ALLOC();
961 }
962
963 end = ptr = buf + sizeof(buf) - 1;
964 *ptr = '\0';
965
966 do {
967 *--ptr = digits[(int) fmod(fvalue, base)];
968 fvalue /= base;
969 } while (ptr > buf && fabs(fvalue) >= 1);
970
971 return zend_string_init(ptr, end - ptr, 0);
972 }
973
974 return _php_math_longtobase(arg, base);
975 }
976 /* }}} */
977
978 /* {{{ proto int bindec(string binary_number)
979 Returns the decimal equivalent of the binary number */
PHP_FUNCTION(bindec)980 PHP_FUNCTION(bindec)
981 {
982 zval *arg;
983
984 ZEND_PARSE_PARAMETERS_START(1, 1)
985 Z_PARAM_ZVAL(arg)
986 ZEND_PARSE_PARAMETERS_END();
987
988 convert_to_string_ex(arg);
989 if (_php_math_basetozval(arg, 2, return_value) == FAILURE) {
990 RETURN_FALSE;
991 }
992 }
993 /* }}} */
994
995 /* {{{ proto int hexdec(string hexadecimal_number)
996 Returns the decimal equivalent of the hexadecimal number */
PHP_FUNCTION(hexdec)997 PHP_FUNCTION(hexdec)
998 {
999 zval *arg;
1000
1001 ZEND_PARSE_PARAMETERS_START(1, 1)
1002 Z_PARAM_ZVAL(arg)
1003 ZEND_PARSE_PARAMETERS_END();
1004
1005 convert_to_string_ex(arg);
1006 if (_php_math_basetozval(arg, 16, return_value) == FAILURE) {
1007 RETURN_FALSE;
1008 }
1009 }
1010 /* }}} */
1011
1012 /* {{{ proto int octdec(string octal_number)
1013 Returns the decimal equivalent of an octal string */
PHP_FUNCTION(octdec)1014 PHP_FUNCTION(octdec)
1015 {
1016 zval *arg;
1017
1018 ZEND_PARSE_PARAMETERS_START(1, 1)
1019 Z_PARAM_ZVAL(arg)
1020 ZEND_PARSE_PARAMETERS_END();
1021
1022 convert_to_string_ex(arg);
1023 if (_php_math_basetozval(arg, 8, return_value) == FAILURE) {
1024 RETURN_FALSE;
1025 }
1026 }
1027 /* }}} */
1028
1029 /* {{{ proto string decbin(int decimal_number)
1030 Returns a string containing a binary representation of the number */
PHP_FUNCTION(decbin)1031 PHP_FUNCTION(decbin)
1032 {
1033 zval *arg;
1034 zend_string *result;
1035
1036 ZEND_PARSE_PARAMETERS_START(1, 1)
1037 Z_PARAM_ZVAL(arg)
1038 ZEND_PARSE_PARAMETERS_END();
1039
1040 convert_to_long_ex(arg);
1041 result = _php_math_longtobase(arg, 2);
1042 RETURN_STR(result);
1043 }
1044 /* }}} */
1045
1046 /* {{{ proto string decoct(int decimal_number)
1047 Returns a string containing an octal representation of the given number */
PHP_FUNCTION(decoct)1048 PHP_FUNCTION(decoct)
1049 {
1050 zval *arg;
1051 zend_string *result;
1052
1053 ZEND_PARSE_PARAMETERS_START(1, 1)
1054 Z_PARAM_ZVAL(arg)
1055 ZEND_PARSE_PARAMETERS_END();
1056
1057 convert_to_long_ex(arg);
1058 result = _php_math_longtobase(arg, 8);
1059 RETURN_STR(result);
1060 }
1061 /* }}} */
1062
1063 /* {{{ proto string dechex(int decimal_number)
1064 Returns a string containing a hexadecimal representation of the given number */
PHP_FUNCTION(dechex)1065 PHP_FUNCTION(dechex)
1066 {
1067 zval *arg;
1068 zend_string *result;
1069
1070 ZEND_PARSE_PARAMETERS_START(1, 1)
1071 Z_PARAM_ZVAL(arg)
1072 ZEND_PARSE_PARAMETERS_END();
1073
1074 convert_to_long_ex(arg);
1075 result = _php_math_longtobase(arg, 16);
1076 RETURN_STR(result);
1077 }
1078 /* }}} */
1079
1080 /* {{{ proto string base_convert(string number, int frombase, int tobase)
1081 Converts a number in a string from any base <= 36 to any base <= 36 */
PHP_FUNCTION(base_convert)1082 PHP_FUNCTION(base_convert)
1083 {
1084 zval *number, temp;
1085 zend_long frombase, tobase;
1086 zend_string *result;
1087
1088 ZEND_PARSE_PARAMETERS_START(3, 3)
1089 Z_PARAM_ZVAL(number)
1090 Z_PARAM_LONG(frombase)
1091 Z_PARAM_LONG(tobase)
1092 ZEND_PARSE_PARAMETERS_END();
1093 convert_to_string_ex(number);
1094
1095 if (frombase < 2 || frombase > 36) {
1096 php_error_docref(NULL, E_WARNING, "Invalid `from base' (" ZEND_LONG_FMT ")", frombase);
1097 RETURN_FALSE;
1098 }
1099 if (tobase < 2 || tobase > 36) {
1100 php_error_docref(NULL, E_WARNING, "Invalid `to base' (" ZEND_LONG_FMT ")", tobase);
1101 RETURN_FALSE;
1102 }
1103
1104 if(_php_math_basetozval(number, (int)frombase, &temp) == FAILURE) {
1105 RETURN_FALSE;
1106 }
1107 result = _php_math_zvaltobase(&temp, (int)tobase);
1108 RETVAL_STR(result);
1109 }
1110 /* }}} */
1111
1112 /* {{{ _php_math_number_format
1113 */
_php_math_number_format(double d,int dec,char dec_point,char thousand_sep)1114 PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1115 {
1116 return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1117 }
1118
_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)1119 PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, char *dec_point,
1120 size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len)
1121 {
1122 zend_string *res;
1123 zend_string *tmpbuf;
1124 char *s, *t; /* source, target */
1125 char *dp;
1126 size_t integral;
1127 size_t reslen = 0;
1128 int count = 0;
1129 int is_negative=0;
1130
1131 if (d < 0) {
1132 is_negative = 1;
1133 d = -d;
1134 }
1135
1136 dec = MAX(0, dec);
1137 d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1138 tmpbuf = strpprintf(0, "%.*F", dec, d);
1139 if (tmpbuf == NULL) {
1140 return NULL;
1141 } else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) {
1142 return tmpbuf;
1143 }
1144
1145 /* Check if the number is no longer negative after rounding */
1146 if (is_negative && d == 0) {
1147 is_negative = 0;
1148 }
1149
1150 /* find decimal point, if expected */
1151 if (dec) {
1152 dp = strpbrk(ZSTR_VAL(tmpbuf), ".,");
1153 } else {
1154 dp = NULL;
1155 }
1156
1157 /* calculate the length of the return buffer */
1158 if (dp) {
1159 integral = (dp - ZSTR_VAL(tmpbuf));
1160 } else {
1161 /* no decimal point was found */
1162 integral = ZSTR_LEN(tmpbuf);
1163 }
1164
1165 /* allow for thousand separators */
1166 if (thousand_sep) {
1167 integral = zend_safe_addmult((integral-1)/3, thousand_sep_len, integral, "number formatting");
1168 }
1169
1170 reslen = integral;
1171
1172 if (dec) {
1173 reslen += dec;
1174
1175 if (dec_point) {
1176 reslen = zend_safe_addmult(reslen, 1, dec_point_len, "number formatting");
1177 }
1178 }
1179
1180 /* add a byte for minus sign */
1181 if (is_negative) {
1182 reslen++;
1183 }
1184 res = zend_string_alloc(reslen, 0);
1185
1186 s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
1187 t = ZSTR_VAL(res) + reslen;
1188 *t-- = '\0';
1189
1190 /* copy the decimal places.
1191 * Take care, as the sprintf implementation may return less places than
1192 * we requested due to internal buffer limitations */
1193 if (dec) {
1194 size_t declen = (dp ? s - dp : 0);
1195 size_t topad = (size_t)dec > declen ? dec - declen : 0;
1196
1197 /* pad with '0's */
1198 while (topad--) {
1199 *t-- = '0';
1200 }
1201
1202 if (dp) {
1203 s -= declen + 1; /* +1 to skip the point */
1204 t -= declen;
1205
1206 /* now copy the chars after the point */
1207 memcpy(t + 1, dp + 1, declen);
1208 }
1209
1210 /* add decimal point */
1211 if (dec_point) {
1212 t -= dec_point_len;
1213 memcpy(t + 1, dec_point, dec_point_len);
1214 }
1215 }
1216
1217 /* copy the numbers before the decimal point, adding thousand
1218 * separator every three digits */
1219 while (s >= ZSTR_VAL(tmpbuf)) {
1220 *t-- = *s--;
1221 if (thousand_sep && (++count%3)==0 && s >= ZSTR_VAL(tmpbuf)) {
1222 t -= thousand_sep_len;
1223 memcpy(t + 1, thousand_sep, thousand_sep_len);
1224 }
1225 }
1226
1227 /* and a minus sign, if needed */
1228 if (is_negative) {
1229 *t-- = '-';
1230 }
1231
1232 ZSTR_LEN(res) = reslen;
1233 zend_string_release_ex(tmpbuf, 0);
1234 return res;
1235 }
1236
1237 /* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_separator, string thousands_separator]])
1238 Formats a number with grouped thousands */
PHP_FUNCTION(number_format)1239 PHP_FUNCTION(number_format)
1240 {
1241 double num;
1242 zend_long dec = 0;
1243 char *thousand_sep = NULL, *dec_point = NULL;
1244 char thousand_sep_chr = ',', dec_point_chr = '.';
1245 size_t thousand_sep_len = 0, dec_point_len = 0;
1246
1247 ZEND_PARSE_PARAMETERS_START(1, 4)
1248 Z_PARAM_DOUBLE(num)
1249 Z_PARAM_OPTIONAL
1250 Z_PARAM_LONG(dec)
1251 Z_PARAM_STRING_EX(dec_point, dec_point_len, 1, 0)
1252 Z_PARAM_STRING_EX(thousand_sep, thousand_sep_len, 1, 0)
1253 ZEND_PARSE_PARAMETERS_END();
1254
1255 switch(ZEND_NUM_ARGS()) {
1256 case 1:
1257 RETURN_STR(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr));
1258 break;
1259 case 2:
1260 RETURN_STR(_php_math_number_format(num, (int)dec, dec_point_chr, thousand_sep_chr));
1261 break;
1262 case 4:
1263 if (dec_point == NULL) {
1264 dec_point = &dec_point_chr;
1265 dec_point_len = 1;
1266 }
1267
1268 if (thousand_sep == NULL) {
1269 thousand_sep = &thousand_sep_chr;
1270 thousand_sep_len = 1;
1271 }
1272
1273 RETVAL_STR(_php_math_number_format_ex(num, (int)dec,
1274 dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1275 break;
1276 default:
1277 WRONG_PARAM_COUNT;
1278 }
1279 }
1280 /* }}} */
1281
1282 /* {{{ proto float fmod(float x, float y)
1283 Returns the remainder of dividing x by y as a float */
PHP_FUNCTION(fmod)1284 PHP_FUNCTION(fmod)
1285 {
1286 double num1, num2;
1287
1288 ZEND_PARSE_PARAMETERS_START(2, 2)
1289 Z_PARAM_DOUBLE(num1)
1290 Z_PARAM_DOUBLE(num2)
1291 ZEND_PARSE_PARAMETERS_END();
1292
1293 RETURN_DOUBLE(fmod(num1, num2));
1294 }
1295 /* }}} */
1296
1297 /* {{{ proto int intdiv(int dividend, int divisor)
1298 Returns the integer quotient of the division of dividend by divisor */
PHP_FUNCTION(intdiv)1299 PHP_FUNCTION(intdiv)
1300 {
1301 zend_long dividend, divisor;
1302
1303 ZEND_PARSE_PARAMETERS_START(2, 2)
1304 Z_PARAM_LONG(dividend)
1305 Z_PARAM_LONG(divisor)
1306 ZEND_PARSE_PARAMETERS_END();
1307
1308 if (divisor == 0) {
1309 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1310 return;
1311 } else if (divisor == -1 && dividend == ZEND_LONG_MIN) {
1312 /* Prevent overflow error/crash ... really should not happen:
1313 We don't return a float here as that violates function contract */
1314 zend_throw_exception_ex(zend_ce_arithmetic_error, 0, "Division of PHP_INT_MIN by -1 is not an integer");
1315 return;
1316 }
1317
1318 RETURN_LONG(dividend / divisor);
1319 }
1320 /* }}} */
1321
1322 /*
1323 * Local variables:
1324 * tab-width: 4
1325 * c-basic-offset: 4
1326 * End:
1327 * vim600: fdm=marker
1328 * vim: noet sw=4 ts=4
1329 */
1330