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