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