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