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 | http://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 SIZEOF_ZEND_LONG > SIZEOF_INT
287 if (precision >= 0) {
288 places = precision > INT_MAX ? INT_MAX : (int)precision;
289 } else {
290 places = precision <= INT_MIN ? INT_MIN+1 : (int)precision;
291 }
292 #else
293 places = precision;
294 #endif
295 }
296
297 switch (Z_TYPE_P(value)) {
298 case IS_LONG:
299 /* Simple case - long that doesn't need to be rounded. */
300 if (places >= 0) {
301 RETURN_DOUBLE((double) Z_LVAL_P(value));
302 }
303 /* break omitted intentionally */
304
305 case IS_DOUBLE:
306 return_val = (Z_TYPE_P(value) == IS_LONG) ? (double)Z_LVAL_P(value) : Z_DVAL_P(value);
307 return_val = _php_math_round(return_val, (int)places, (int)mode);
308 RETURN_DOUBLE(return_val);
309 break;
310
311 EMPTY_SWITCH_DEFAULT_CASE()
312 }
313 }
314 /* }}} */
315
316 /* {{{ Returns the sine of the number in radians */
PHP_FUNCTION(sin)317 PHP_FUNCTION(sin)
318 {
319 double num;
320
321 ZEND_PARSE_PARAMETERS_START(1, 1)
322 Z_PARAM_DOUBLE(num)
323 ZEND_PARSE_PARAMETERS_END();
324 RETURN_DOUBLE(sin(num));
325 }
326 /* }}} */
327
328 /* {{{ Returns the cosine of the number in radians */
PHP_FUNCTION(cos)329 PHP_FUNCTION(cos)
330 {
331 double num;
332
333 ZEND_PARSE_PARAMETERS_START(1, 1)
334 Z_PARAM_DOUBLE(num)
335 ZEND_PARSE_PARAMETERS_END();
336 RETURN_DOUBLE(cos(num));
337 }
338 /* }}} */
339
340 /* {{{ Returns the tangent of the number in radians */
PHP_FUNCTION(tan)341 PHP_FUNCTION(tan)
342 {
343 double num;
344
345 ZEND_PARSE_PARAMETERS_START(1, 1)
346 Z_PARAM_DOUBLE(num)
347 ZEND_PARSE_PARAMETERS_END();
348 RETURN_DOUBLE(tan(num));
349 }
350 /* }}} */
351
352 /* {{{ Returns the arc sine of the number in radians */
PHP_FUNCTION(asin)353 PHP_FUNCTION(asin)
354 {
355 double num;
356
357 ZEND_PARSE_PARAMETERS_START(1, 1)
358 Z_PARAM_DOUBLE(num)
359 ZEND_PARSE_PARAMETERS_END();
360 RETURN_DOUBLE(asin(num));
361 }
362 /* }}} */
363
364 /* {{{ Return the arc cosine of the number in radians */
PHP_FUNCTION(acos)365 PHP_FUNCTION(acos)
366 {
367 double num;
368
369 ZEND_PARSE_PARAMETERS_START(1, 1)
370 Z_PARAM_DOUBLE(num)
371 ZEND_PARSE_PARAMETERS_END();
372 RETURN_DOUBLE(acos(num));
373 }
374 /* }}} */
375
376 /* {{{ Returns the arc tangent of the number in radians */
PHP_FUNCTION(atan)377 PHP_FUNCTION(atan)
378 {
379 double num;
380
381 ZEND_PARSE_PARAMETERS_START(1, 1)
382 Z_PARAM_DOUBLE(num)
383 ZEND_PARSE_PARAMETERS_END();
384 RETURN_DOUBLE(atan(num));
385 }
386 /* }}} */
387
388 /* {{{ Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
PHP_FUNCTION(atan2)389 PHP_FUNCTION(atan2)
390 {
391 double num1, num2;
392
393 ZEND_PARSE_PARAMETERS_START(2, 2)
394 Z_PARAM_DOUBLE(num1)
395 Z_PARAM_DOUBLE(num2)
396 ZEND_PARSE_PARAMETERS_END();
397 RETURN_DOUBLE(atan2(num1, num2));
398 }
399 /* }}} */
400
401 /* {{{ Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
PHP_FUNCTION(sinh)402 PHP_FUNCTION(sinh)
403 {
404 double num;
405
406 ZEND_PARSE_PARAMETERS_START(1, 1)
407 Z_PARAM_DOUBLE(num)
408 ZEND_PARSE_PARAMETERS_END();
409 RETURN_DOUBLE(sinh(num));
410 }
411 /* }}} */
412
413 /* {{{ Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
PHP_FUNCTION(cosh)414 PHP_FUNCTION(cosh)
415 {
416 double num;
417
418 ZEND_PARSE_PARAMETERS_START(1, 1)
419 Z_PARAM_DOUBLE(num)
420 ZEND_PARSE_PARAMETERS_END();
421 RETURN_DOUBLE(cosh(num));
422 }
423 /* }}} */
424
425 /* {{{ Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
PHP_FUNCTION(tanh)426 PHP_FUNCTION(tanh)
427 {
428 double num;
429
430 ZEND_PARSE_PARAMETERS_START(1, 1)
431 Z_PARAM_DOUBLE(num)
432 ZEND_PARSE_PARAMETERS_END();
433 RETURN_DOUBLE(tanh(num));
434 }
435 /* }}} */
436
437 /* {{{ Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
PHP_FUNCTION(asinh)438 PHP_FUNCTION(asinh)
439 {
440 double num;
441
442 ZEND_PARSE_PARAMETERS_START(1, 1)
443 Z_PARAM_DOUBLE(num)
444 ZEND_PARSE_PARAMETERS_END();
445 RETURN_DOUBLE(asinh(num));
446 }
447 /* }}} */
448
449 /* {{{ Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
PHP_FUNCTION(acosh)450 PHP_FUNCTION(acosh)
451 {
452 double num;
453
454 ZEND_PARSE_PARAMETERS_START(1, 1)
455 Z_PARAM_DOUBLE(num)
456 ZEND_PARSE_PARAMETERS_END();
457 RETURN_DOUBLE(acosh(num));
458 }
459 /* }}} */
460
461 /* {{{ Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
PHP_FUNCTION(atanh)462 PHP_FUNCTION(atanh)
463 {
464 double num;
465
466 ZEND_PARSE_PARAMETERS_START(1, 1)
467 Z_PARAM_DOUBLE(num)
468 ZEND_PARSE_PARAMETERS_END();
469 RETURN_DOUBLE(atanh(num));
470 }
471 /* }}} */
472
473 /* {{{ Returns an approximation of pi */
PHP_FUNCTION(pi)474 PHP_FUNCTION(pi)
475 {
476 ZEND_PARSE_PARAMETERS_NONE();
477
478 RETURN_DOUBLE(M_PI);
479 }
480 /* }}} */
481
482 /* {{{ Returns whether argument is finite */
PHP_FUNCTION(is_finite)483 PHP_FUNCTION(is_finite)
484 {
485 double dval;
486
487 ZEND_PARSE_PARAMETERS_START(1, 1)
488 Z_PARAM_DOUBLE(dval)
489 ZEND_PARSE_PARAMETERS_END();
490 RETURN_BOOL(zend_finite(dval));
491 }
492 /* }}} */
493
494 /* {{{ Returns whether argument is infinite */
PHP_FUNCTION(is_infinite)495 PHP_FUNCTION(is_infinite)
496 {
497 double dval;
498
499 ZEND_PARSE_PARAMETERS_START(1, 1)
500 Z_PARAM_DOUBLE(dval)
501 ZEND_PARSE_PARAMETERS_END();
502 RETURN_BOOL(zend_isinf(dval));
503 }
504 /* }}} */
505
506 /* {{{ Returns whether argument is not a number */
PHP_FUNCTION(is_nan)507 PHP_FUNCTION(is_nan)
508 {
509 double dval;
510
511 ZEND_PARSE_PARAMETERS_START(1, 1)
512 Z_PARAM_DOUBLE(dval)
513 ZEND_PARSE_PARAMETERS_END();
514 RETURN_BOOL(zend_isnan(dval));
515 }
516 /* }}} */
517
518 /* {{{ Returns base raised to the power of exponent. Returns integer result when possible */
PHP_FUNCTION(pow)519 PHP_FUNCTION(pow)
520 {
521 zval *zbase, *zexp;
522
523 ZEND_PARSE_PARAMETERS_START(2, 2)
524 Z_PARAM_ZVAL(zbase)
525 Z_PARAM_ZVAL(zexp)
526 ZEND_PARSE_PARAMETERS_END();
527
528 pow_function(return_value, zbase, zexp);
529 }
530 /* }}} */
531
532 /* {{{ Returns e raised to the power of the number */
PHP_FUNCTION(exp)533 PHP_FUNCTION(exp)
534 {
535 double num;
536
537 ZEND_PARSE_PARAMETERS_START(1, 1)
538 Z_PARAM_DOUBLE(num)
539 ZEND_PARSE_PARAMETERS_END();
540
541 RETURN_DOUBLE(exp(num));
542 }
543 /* }}} */
544
545 /* {{{ Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
PHP_FUNCTION(expm1)546 PHP_FUNCTION(expm1)
547 {
548 double num;
549
550 ZEND_PARSE_PARAMETERS_START(1, 1)
551 Z_PARAM_DOUBLE(num)
552 ZEND_PARSE_PARAMETERS_END();
553
554 RETURN_DOUBLE(expm1(num));
555 }
556 /* }}} */
557
558 /* {{{ Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
PHP_FUNCTION(log1p)559 PHP_FUNCTION(log1p)
560 {
561 double num;
562
563 ZEND_PARSE_PARAMETERS_START(1, 1)
564 Z_PARAM_DOUBLE(num)
565 ZEND_PARSE_PARAMETERS_END();
566
567 RETURN_DOUBLE(log1p(num));
568 }
569 /* }}} */
570
571 /* {{{ Returns the natural logarithm of the number, or the base log if base is specified */
PHP_FUNCTION(log)572 PHP_FUNCTION(log)
573 {
574 double num, base = 0;
575
576 ZEND_PARSE_PARAMETERS_START(1, 2)
577 Z_PARAM_DOUBLE(num)
578 Z_PARAM_OPTIONAL
579 Z_PARAM_DOUBLE(base)
580 ZEND_PARSE_PARAMETERS_END();
581
582 if (ZEND_NUM_ARGS() == 1) {
583 RETURN_DOUBLE(log(num));
584 }
585
586 if (base == 2.0) {
587 RETURN_DOUBLE(log2(num));
588 }
589
590 if (base == 10.0) {
591 RETURN_DOUBLE(log10(num));
592 }
593
594 if (base == 1.0) {
595 RETURN_DOUBLE(ZEND_NAN);
596 }
597
598 if (base <= 0.0) {
599 zend_argument_value_error(2, "must be greater than 0");
600 RETURN_THROWS();
601 }
602
603 RETURN_DOUBLE(log(num) / log(base));
604 }
605 /* }}} */
606
607 /* {{{ Returns the base-10 logarithm of the number */
PHP_FUNCTION(log10)608 PHP_FUNCTION(log10)
609 {
610 double num;
611
612 ZEND_PARSE_PARAMETERS_START(1, 1)
613 Z_PARAM_DOUBLE(num)
614 ZEND_PARSE_PARAMETERS_END();
615
616 RETURN_DOUBLE(log10(num));
617 }
618 /* }}} */
619
620 /* {{{ Returns the square root of the number */
PHP_FUNCTION(sqrt)621 PHP_FUNCTION(sqrt)
622 {
623 double num;
624
625 ZEND_PARSE_PARAMETERS_START(1, 1)
626 Z_PARAM_DOUBLE(num)
627 ZEND_PARSE_PARAMETERS_END();
628
629 RETURN_DOUBLE(sqrt(num));
630 }
631 /* }}} */
632
633 /* {{{ Returns sqrt(num1*num1 + num2*num2) */
PHP_FUNCTION(hypot)634 PHP_FUNCTION(hypot)
635 {
636 double num1, num2;
637
638 ZEND_PARSE_PARAMETERS_START(2, 2)
639 Z_PARAM_DOUBLE(num1)
640 Z_PARAM_DOUBLE(num2)
641 ZEND_PARSE_PARAMETERS_END();
642
643 RETURN_DOUBLE(hypot(num1, num2));
644 }
645 /* }}} */
646
647 /* {{{ Converts the number in degrees to the radian equivalent */
PHP_FUNCTION(deg2rad)648 PHP_FUNCTION(deg2rad)
649 {
650 double deg;
651
652 ZEND_PARSE_PARAMETERS_START(1, 1)
653 Z_PARAM_DOUBLE(deg)
654 ZEND_PARSE_PARAMETERS_END();
655 RETURN_DOUBLE((deg / 180.0) * M_PI);
656 }
657 /* }}} */
658
659 /* {{{ Converts the radian number to the equivalent number in degrees */
PHP_FUNCTION(rad2deg)660 PHP_FUNCTION(rad2deg)
661 {
662 double rad;
663
664 ZEND_PARSE_PARAMETERS_START(1, 1)
665 Z_PARAM_DOUBLE(rad)
666 ZEND_PARSE_PARAMETERS_END();
667
668 RETURN_DOUBLE((rad / M_PI) * 180);
669 }
670 /* }}} */
671
672 /* {{{ _php_math_basetolong */
673 /*
674 * Convert a string representation of a base(2-36) number to a long.
675 */
_php_math_basetolong(zval * arg,int base)676 PHPAPI zend_long _php_math_basetolong(zval *arg, int base)
677 {
678 zend_long num = 0, digit, onum;
679 zend_long i;
680 char c, *s;
681
682 if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
683 return 0;
684 }
685
686 s = Z_STRVAL_P(arg);
687
688 for (i = Z_STRLEN_P(arg); i > 0; i--) {
689 c = *s++;
690
691 digit = (c >= '0' && c <= '9') ? c - '0'
692 : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
693 : (c >= 'a' && c <= 'z') ? c - 'a' + 10
694 : base;
695
696 if (digit >= base) {
697 continue;
698 }
699
700 onum = num;
701 num = num * base + digit;
702 if (num > onum)
703 continue;
704
705 {
706
707 php_error_docref(NULL, E_WARNING, "Number %s is too big to fit in long", s);
708 return ZEND_LONG_MAX;
709 }
710 }
711
712 return num;
713 }
714 /* }}} */
715
716 /* {{{ _php_math_basetozval */
717 /*
718 * Convert a string representation of a base(2-36) number to a zval.
719 */
_php_math_basetozval(zend_string * str,int base,zval * ret)720 PHPAPI void _php_math_basetozval(zend_string *str, int base, zval *ret)
721 {
722 zend_long num = 0;
723 double fnum = 0;
724 int mode = 0;
725 char c, *s, *e;
726 zend_long cutoff;
727 int cutlim;
728 int invalidchars = 0;
729
730 s = ZSTR_VAL(str);
731 e = s + ZSTR_LEN(str);
732
733 /* Skip leading whitespace */
734 while (s < e && isspace(*s)) s++;
735 /* Skip trailing whitespace */
736 while (s < e && isspace(*(e-1))) e--;
737
738 if (e - s >= 2) {
739 if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2;
740 if (base == 8 && s[0] == '0' && (s[1] == 'o' || s[1] == 'O')) s += 2;
741 if (base == 2 && s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) s += 2;
742 }
743
744 cutoff = ZEND_LONG_MAX / base;
745 cutlim = ZEND_LONG_MAX % base;
746
747 while (s < e) {
748 c = *s++;
749
750 /* might not work for EBCDIC */
751 if (c >= '0' && c <= '9')
752 c -= '0';
753 else if (c >= 'A' && c <= 'Z')
754 c -= 'A' - 10;
755 else if (c >= 'a' && c <= 'z')
756 c -= 'a' - 10;
757 else {
758 invalidchars++;
759 continue;
760 }
761
762 if (c >= base) {
763 invalidchars++;
764 continue;
765 }
766
767 switch (mode) {
768 case 0: /* Integer */
769 if (num < cutoff || (num == cutoff && c <= cutlim)) {
770 num = num * base + c;
771 break;
772 } else {
773 fnum = (double)num;
774 mode = 1;
775 }
776 /* fall-through */
777 case 1: /* Float */
778 fnum = fnum * base + c;
779 }
780 }
781
782 if (invalidchars > 0) {
783 zend_error(E_DEPRECATED, "Invalid characters passed for attempted conversion, these have been ignored");
784 }
785
786 if (mode == 1) {
787 ZVAL_DOUBLE(ret, fnum);
788 } else {
789 ZVAL_LONG(ret, num);
790 }
791 }
792 /* }}} */
793
794 /* {{{ _php_math_longtobase */
795 /*
796 * Convert a long to a string containing a base(2-36) representation of
797 * the number.
798 */
_php_math_longtobase(zend_long arg,int base)799 PHPAPI zend_string * _php_math_longtobase(zend_long arg, int base)
800 {
801 static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
802 char buf[(sizeof(zend_ulong) << 3) + 1];
803 char *ptr, *end;
804 zend_ulong value;
805
806 if (base < 2 || base > 36) {
807 return ZSTR_EMPTY_ALLOC();
808 }
809
810 value = arg;
811
812 end = ptr = buf + sizeof(buf) - 1;
813 *ptr = '\0';
814
815 do {
816 ZEND_ASSERT(ptr > buf);
817 *--ptr = digits[value % base];
818 value /= base;
819 } while (value);
820
821 return zend_string_init(ptr, end - ptr, 0);
822 }
823 /* }}} */
824
825 /* {{{ _php_math_longtobase_pwr2 */
826 /*
827 * Convert a long to a string containing a base(2,4,6,16,32) representation of
828 * the number.
829 */
_php_math_longtobase_pwr2(zend_long arg,int base_log2)830 static zend_always_inline zend_string * _php_math_longtobase_pwr2(zend_long arg, int base_log2)
831 {
832 static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
833 zend_ulong value;
834 size_t len;
835 zend_string *ret;
836 char *ptr;
837
838 value = arg;
839
840 if (value == 0) {
841 len = 1;
842 } else {
843 len = ((sizeof(value) * 8 - zend_ulong_nlz(value)) + (base_log2 - 1)) / base_log2;
844 }
845
846 ret = zend_string_alloc(len, 0);
847 ptr = ZSTR_VAL(ret) + len;
848 *ptr = '\0';
849
850 do {
851 ZEND_ASSERT(ptr > ZSTR_VAL(ret));
852 *--ptr = digits[value & ((1 << base_log2) - 1)];
853 value >>= base_log2;
854 } while (value);
855
856 return ret;
857 }
858 /* }}} */
859
860 /* {{{ _php_math_zvaltobase */
861 /*
862 * Convert a zval to a string containing a base(2-36) representation of
863 * the number.
864 */
_php_math_zvaltobase(zval * arg,int base)865 PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base)
866 {
867 static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
868
869 if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
870 return ZSTR_EMPTY_ALLOC();
871 }
872
873 if (Z_TYPE_P(arg) == IS_DOUBLE) {
874 double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
875 char *ptr, *end;
876 char buf[(sizeof(double) << 3) + 1];
877
878 /* Don't try to convert +/- infinity */
879 if (fvalue == ZEND_INFINITY || fvalue == -ZEND_INFINITY) {
880 zend_value_error("An infinite value cannot be converted to base %d", base);
881 return NULL;
882 }
883
884 end = ptr = buf + sizeof(buf) - 1;
885 *ptr = '\0';
886
887 do {
888 *--ptr = digits[(int) fmod(fvalue, base)];
889 fvalue /= base;
890 } while (ptr > buf && fabs(fvalue) >= 1);
891
892 return zend_string_init(ptr, end - ptr, 0);
893 }
894
895 return _php_math_longtobase(Z_LVAL_P(arg), base);
896 }
897 /* }}} */
898
899 /* {{{ Returns the decimal equivalent of the binary number */
PHP_FUNCTION(bindec)900 PHP_FUNCTION(bindec)
901 {
902 zend_string *arg;
903
904 ZEND_PARSE_PARAMETERS_START(1, 1)
905 Z_PARAM_STR(arg)
906 ZEND_PARSE_PARAMETERS_END();
907
908 _php_math_basetozval(arg, 2, return_value);
909 }
910 /* }}} */
911
912 /* {{{ Returns the decimal equivalent of the hexadecimal number */
PHP_FUNCTION(hexdec)913 PHP_FUNCTION(hexdec)
914 {
915 zend_string *arg;
916
917 ZEND_PARSE_PARAMETERS_START(1, 1)
918 Z_PARAM_STR(arg)
919 ZEND_PARSE_PARAMETERS_END();
920
921 _php_math_basetozval(arg, 16, return_value);
922 }
923 /* }}} */
924
925 /* {{{ Returns the decimal equivalent of an octal string */
PHP_FUNCTION(octdec)926 PHP_FUNCTION(octdec)
927 {
928 zend_string *arg;
929
930 ZEND_PARSE_PARAMETERS_START(1, 1)
931 Z_PARAM_STR(arg)
932 ZEND_PARSE_PARAMETERS_END();
933
934 _php_math_basetozval(arg, 8, return_value);
935 }
936 /* }}} */
937
938 /* {{{ Returns a string containing a binary representation of the number */
PHP_FUNCTION(decbin)939 PHP_FUNCTION(decbin)
940 {
941 zend_long arg;
942
943 ZEND_PARSE_PARAMETERS_START(1, 1)
944 Z_PARAM_LONG(arg)
945 ZEND_PARSE_PARAMETERS_END();
946
947 RETURN_STR(_php_math_longtobase_pwr2(arg, 1));
948 }
949 /* }}} */
950
951 /* {{{ Returns a string containing an octal representation of the given number */
PHP_FUNCTION(decoct)952 PHP_FUNCTION(decoct)
953 {
954 zend_long arg;
955
956 ZEND_PARSE_PARAMETERS_START(1, 1)
957 Z_PARAM_LONG(arg)
958 ZEND_PARSE_PARAMETERS_END();
959
960 RETURN_STR(_php_math_longtobase_pwr2(arg, 3));
961 }
962 /* }}} */
963
964 /* {{{ Returns a string containing a hexadecimal representation of the given number */
PHP_FUNCTION(dechex)965 PHP_FUNCTION(dechex)
966 {
967 zend_long arg;
968
969 ZEND_PARSE_PARAMETERS_START(1, 1)
970 Z_PARAM_LONG(arg)
971 ZEND_PARSE_PARAMETERS_END();
972
973 RETURN_STR(_php_math_longtobase_pwr2(arg, 4));
974 }
975 /* }}} */
976
977 /* {{{ Converts a number in a string from any base <= 36 to any base <= 36 */
PHP_FUNCTION(base_convert)978 PHP_FUNCTION(base_convert)
979 {
980 zval temp;
981 zend_string *number;
982 zend_long frombase, tobase;
983 zend_string *result;
984
985 ZEND_PARSE_PARAMETERS_START(3, 3)
986 Z_PARAM_STR(number)
987 Z_PARAM_LONG(frombase)
988 Z_PARAM_LONG(tobase)
989 ZEND_PARSE_PARAMETERS_END();
990
991 if (frombase < 2 || frombase > 36) {
992 zend_argument_value_error(2, "must be between 2 and 36 (inclusive)");
993 RETURN_THROWS();
994 }
995 if (tobase < 2 || tobase > 36) {
996 zend_argument_value_error(3, "must be between 2 and 36 (inclusive)");
997 RETURN_THROWS();
998 }
999
1000 _php_math_basetozval(number, (int)frombase, &temp);
1001 result = _php_math_zvaltobase(&temp, (int)tobase);
1002 if (!result) {
1003 RETURN_THROWS();
1004 }
1005
1006 RETVAL_STR(result);
1007 }
1008 /* }}} */
1009
1010 /* {{{ _php_math_number_format */
_php_math_number_format(double d,int dec,char dec_point,char thousand_sep)1011 PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1012 {
1013 return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1014 }
1015
_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)1016 PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *dec_point,
1017 size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len)
1018 {
1019 zend_string *res;
1020 zend_string *tmpbuf;
1021 char *s, *t; /* source, target */
1022 char *dp;
1023 size_t integral;
1024 size_t reslen = 0;
1025 int count = 0;
1026 int is_negative=0;
1027
1028 if (d < 0) {
1029 is_negative = 1;
1030 d = -d;
1031 }
1032
1033 dec = MAX(0, dec);
1034 d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1035 tmpbuf = strpprintf(0, "%.*F", dec, d);
1036 if (tmpbuf == NULL) {
1037 return NULL;
1038 } else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) {
1039 return tmpbuf;
1040 }
1041
1042 /* Check if the number is no longer negative after rounding */
1043 if (is_negative && d == 0) {
1044 is_negative = 0;
1045 }
1046
1047 /* find decimal point, if expected */
1048 if (dec) {
1049 dp = strpbrk(ZSTR_VAL(tmpbuf), ".,");
1050 } else {
1051 dp = NULL;
1052 }
1053
1054 /* calculate the length of the return buffer */
1055 if (dp) {
1056 integral = (dp - ZSTR_VAL(tmpbuf));
1057 } else {
1058 /* no decimal point was found */
1059 integral = ZSTR_LEN(tmpbuf);
1060 }
1061
1062 /* allow for thousand separators */
1063 if (thousand_sep) {
1064 integral = zend_safe_addmult((integral-1)/3, thousand_sep_len, integral, "number formatting");
1065 }
1066
1067 reslen = integral;
1068
1069 if (dec) {
1070 reslen += dec;
1071
1072 if (dec_point) {
1073 reslen = zend_safe_addmult(reslen, 1, dec_point_len, "number formatting");
1074 }
1075 }
1076
1077 /* add a byte for minus sign */
1078 if (is_negative) {
1079 reslen++;
1080 }
1081 res = zend_string_alloc(reslen, 0);
1082
1083 s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
1084 t = ZSTR_VAL(res) + reslen;
1085 *t-- = '\0';
1086
1087 /* copy the decimal places.
1088 * Take care, as the sprintf implementation may return less places than
1089 * we requested due to internal buffer limitations */
1090 if (dec) {
1091 size_t declen = (dp ? s - dp : 0);
1092 size_t topad = (size_t)dec > declen ? dec - declen : 0;
1093
1094 /* pad with '0's */
1095 while (topad--) {
1096 *t-- = '0';
1097 }
1098
1099 if (dp) {
1100 s -= declen + 1; /* +1 to skip the point */
1101 t -= declen;
1102
1103 /* now copy the chars after the point */
1104 memcpy(t + 1, dp + 1, declen);
1105 }
1106
1107 /* add decimal point */
1108 if (dec_point) {
1109 t -= dec_point_len;
1110 memcpy(t + 1, dec_point, dec_point_len);
1111 }
1112 }
1113
1114 /* copy the numbers before the decimal point, adding thousand
1115 * separator every three digits */
1116 while (s >= ZSTR_VAL(tmpbuf)) {
1117 *t-- = *s--;
1118 if (thousand_sep && (++count%3)==0 && s >= ZSTR_VAL(tmpbuf)) {
1119 t -= thousand_sep_len;
1120 memcpy(t + 1, thousand_sep, thousand_sep_len);
1121 }
1122 }
1123
1124 /* and a minus sign, if needed */
1125 if (is_negative) {
1126 *t-- = '-';
1127 }
1128
1129 ZSTR_LEN(res) = reslen;
1130 zend_string_release_ex(tmpbuf, 0);
1131 return res;
1132 }
1133
1134 /* {{{ Formats a number with grouped thousands */
PHP_FUNCTION(number_format)1135 PHP_FUNCTION(number_format)
1136 {
1137 double num;
1138 zend_long dec = 0;
1139 char *thousand_sep = NULL, *dec_point = NULL;
1140 size_t thousand_sep_len = 0, dec_point_len = 0;
1141
1142 ZEND_PARSE_PARAMETERS_START(1, 4)
1143 Z_PARAM_DOUBLE(num)
1144 Z_PARAM_OPTIONAL
1145 Z_PARAM_LONG(dec)
1146 Z_PARAM_STRING_OR_NULL(dec_point, dec_point_len)
1147 Z_PARAM_STRING_OR_NULL(thousand_sep, thousand_sep_len)
1148 ZEND_PARSE_PARAMETERS_END();
1149
1150 if (dec_point == NULL) {
1151 dec_point = ".";
1152 dec_point_len = 1;
1153 }
1154 if (thousand_sep == NULL) {
1155 thousand_sep = ",";
1156 thousand_sep_len = 1;
1157 }
1158
1159 RETURN_STR(_php_math_number_format_ex(num, (int)dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1160 }
1161 /* }}} */
1162
1163 /* {{{ Returns the remainder of dividing x by y as a float */
PHP_FUNCTION(fmod)1164 PHP_FUNCTION(fmod)
1165 {
1166 double num1, num2;
1167
1168 ZEND_PARSE_PARAMETERS_START(2, 2)
1169 Z_PARAM_DOUBLE(num1)
1170 Z_PARAM_DOUBLE(num2)
1171 ZEND_PARSE_PARAMETERS_END();
1172
1173 RETURN_DOUBLE(fmod(num1, num2));
1174 }
1175 /* }}} */
1176
1177 /* {{{ Perform floating-point division of dividend / divisor
1178 with IEEE-754 semantics for division by zero. */
1179 #ifdef __clang__
1180 __attribute__((no_sanitize("float-divide-by-zero")))
1181 #endif
PHP_FUNCTION(fdiv)1182 PHP_FUNCTION(fdiv)
1183 {
1184 double dividend, divisor;
1185
1186 ZEND_PARSE_PARAMETERS_START(2, 2)
1187 Z_PARAM_DOUBLE(dividend)
1188 Z_PARAM_DOUBLE(divisor)
1189 ZEND_PARSE_PARAMETERS_END();
1190
1191 RETURN_DOUBLE(dividend / divisor);
1192 }
1193 /* }}} */
1194
1195 /* {{{ Returns the integer quotient of the division of dividend by divisor */
PHP_FUNCTION(intdiv)1196 PHP_FUNCTION(intdiv)
1197 {
1198 zend_long dividend, divisor;
1199
1200 ZEND_PARSE_PARAMETERS_START(2, 2)
1201 Z_PARAM_LONG(dividend)
1202 Z_PARAM_LONG(divisor)
1203 ZEND_PARSE_PARAMETERS_END();
1204
1205 if (divisor == 0) {
1206 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1207 RETURN_THROWS();
1208 } else if (divisor == -1 && dividend == ZEND_LONG_MIN) {
1209 /* Prevent overflow error/crash ... really should not happen:
1210 We don't return a float here as that violates function contract */
1211 zend_throw_exception_ex(zend_ce_arithmetic_error, 0, "Division of PHP_INT_MIN by -1 is not an integer");
1212 RETURN_THROWS();
1213 }
1214
1215 RETURN_LONG(dividend / divisor);
1216 }
1217 /* }}} */
1218