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