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