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