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