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