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