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