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