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