1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
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#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 */
37static 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 */
82static 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 */
97static 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 */
110static 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 */
140PHPAPI 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    precision_places = 14 - php_intlog10abs(value);
150
151    f1 = php_intpow10(abs(places));
152
153    /* If the decimal precision guaranteed by FP arithmetic is higher than
154       the requested places BUT is small enough to make sure a non-zero value
155       is returned, pre-round the result to the precision */
156    if (precision_places > places && precision_places - places < 15) {
157        f2 = php_intpow10(abs(precision_places));
158        if (precision_places >= 0) {
159            tmp_value = value * f2;
160        } else {
161            tmp_value = value / f2;
162        }
163        /* preround the result (tmp_value will always be something * 1e14,
164           thus never larger than 1e15 here) */
165        tmp_value = php_round_helper(tmp_value, mode);
166        /* now correctly move the decimal point */
167        f2 = php_intpow10(abs(places - precision_places));
168        /* because places < precision_places */
169        tmp_value = tmp_value / f2;
170    } else {
171        /* adjust the value */
172        if (places >= 0) {
173            tmp_value = value * f1;
174        } else {
175            tmp_value = value / f1;
176        }
177        /* This value is beyond our precision, so rounding it is pointless */
178        if (fabs(tmp_value) >= 1e15) {
179            return value;
180        }
181    }
182
183    /* round the temp value */
184    tmp_value = php_round_helper(tmp_value, mode);
185
186    /* see if it makes sense to use simple division to round the value */
187    if (abs(places) < 23) {
188        if (places > 0) {
189            tmp_value = tmp_value / f1;
190        } else {
191            tmp_value = tmp_value * f1;
192        }
193    } else {
194        /* Simple division can't be used since that will cause wrong results.
195           Instead, the number is converted to a string and back again using
196           strtod(). strtod() will return the nearest possible FP value for
197           that string. */
198
199        /* 40 Bytes should be more than enough for this format string. The
200           float won't be larger than 1e15 anyway. But just in case, use
201           snprintf() and make sure the buffer is zero-terminated */
202        char buf[40];
203        snprintf(buf, 39, "%15fe%d", tmp_value, -places);
204        buf[39] = '\0';
205        tmp_value = zend_strtod(buf, NULL);
206        /* couldn't convert to string and back */
207        if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
208            tmp_value = value;
209        }
210    }
211
212    return tmp_value;
213}
214/* }}} */
215
216/* {{{ php_asinh
217*/
218static double php_asinh(double z)
219{
220#ifdef HAVE_ASINH
221    return(asinh(z));
222#else
223# ifdef _WIN64
224    if (z >= 0) {
225        return log(z + sqrt(z * z + 1));
226    }
227    else {
228        return -log(-z + sqrt(z * z + 1));
229    }
230# else
231    return(log(z + sqrt(1 + pow(z, 2))) / log(M_E));
232# endif
233#endif
234}
235/* }}} */
236
237/* {{{ php_acosh
238*/
239static double php_acosh(double x)
240{
241#ifdef HAVE_ACOSH
242    return(acosh(x));
243#else
244# ifdef _WIN64
245    if (x >= 1) {
246        return log(x + sqrt(x * x - 1));
247    } else {
248        return (DBL_MAX+DBL_MAX)-(DBL_MAX+DBL_MAX);
249    }
250# else
251    return(log(x + sqrt(x * x - 1)));
252# endif
253#endif
254}
255/* }}} */
256
257/* {{{ php_atanh
258*/
259static double php_atanh(double z)
260{
261#ifdef HAVE_ATANH
262    return(atanh(z));
263#else
264    return(0.5 * log((1 + z) / (1 - z)));
265#endif
266}
267/* }}} */
268
269/* {{{ php_log1p
270*/
271static double php_log1p(double x)
272{
273#ifdef HAVE_LOG1P
274    return(log1p(x));
275#else
276    return(log(1 + x));
277#endif
278}
279/* }}} */
280
281/* {{{ php_expm1
282*/
283static double php_expm1(double x)
284{
285#if !defined(PHP_WIN32) && !defined(NETWARE)
286    return(expm1(x));
287#else
288    return(exp(x) - 1);
289#endif
290}
291/* }}}*/
292
293/* {{{ proto int abs(int number)
294   Return the absolute value of the number */
295PHP_FUNCTION(abs)
296{
297    zval *value;
298
299#ifndef FAST_ZPP
300    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) {
301        return;
302    }
303#else
304    ZEND_PARSE_PARAMETERS_START(1, 1)
305        Z_PARAM_ZVAL(value)
306    ZEND_PARSE_PARAMETERS_END();
307#endif
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 */
326PHP_FUNCTION(ceil)
327{
328    zval *value;
329
330    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) {
331        return;
332    }
333    convert_scalar_to_number_ex(value);
334
335    if (Z_TYPE_P(value) == IS_DOUBLE) {
336        RETURN_DOUBLE(ceil(Z_DVAL_P(value)));
337    } else if (Z_TYPE_P(value) == IS_LONG) {
338        RETURN_DOUBLE(zval_get_double(value));
339    }
340    RETURN_FALSE;
341}
342/* }}} */
343
344/* {{{ proto float floor(float number)
345   Returns the next lowest integer value from the number */
346PHP_FUNCTION(floor)
347{
348    zval *value;
349
350    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) {
351        return;
352    }
353    convert_scalar_to_number_ex(value);
354
355    if (Z_TYPE_P(value) == IS_DOUBLE) {
356        RETURN_DOUBLE(floor(Z_DVAL_P(value)));
357    } else if (Z_TYPE_P(value) == IS_LONG) {
358        RETURN_DOUBLE(zval_get_double(value));
359    }
360    RETURN_FALSE;
361}
362/* }}} */
363
364/* {{{ proto float round(float number [, int precision [, int mode]])
365   Returns the number rounded to specified precision */
366PHP_FUNCTION(round)
367{
368    zval *value;
369    int places = 0;
370    zend_long precision = 0;
371    zend_long mode = PHP_ROUND_HALF_UP;
372    double return_val;
373
374    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|ll", &value, &precision, &mode) == FAILURE) {
375        return;
376    }
377
378    if (ZEND_NUM_ARGS() >= 2) {
379        places = (int) precision;
380    }
381    convert_scalar_to_number_ex(value);
382
383    switch (Z_TYPE_P(value)) {
384        case IS_LONG:
385            /* Simple case - long that doesn't need to be rounded. */
386            if (places >= 0) {
387                RETURN_DOUBLE((double) Z_LVAL_P(value));
388            }
389            /* break omitted intentionally */
390
391        case IS_DOUBLE:
392            return_val = (Z_TYPE_P(value) == IS_LONG) ? (double)Z_LVAL_P(value) : Z_DVAL_P(value);
393            return_val = _php_math_round(return_val, (int)places, (int)mode);
394            RETURN_DOUBLE(return_val);
395            break;
396
397        default:
398            RETURN_FALSE;
399            break;
400    }
401}
402/* }}} */
403
404/* {{{ proto float sin(float number)
405   Returns the sine of the number in radians */
406PHP_FUNCTION(sin)
407{
408    double num;
409
410#ifndef FAST_ZPP
411    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
412        return;
413    }
414#else
415    ZEND_PARSE_PARAMETERS_START(1, 1)
416        Z_PARAM_DOUBLE(num)
417    ZEND_PARSE_PARAMETERS_END();
418#endif
419    RETURN_DOUBLE(sin(num));
420}
421/* }}} */
422
423/* {{{ proto float cos(float number)
424   Returns the cosine of the number in radians */
425PHP_FUNCTION(cos)
426{
427    double num;
428
429#ifndef FAST_ZPP
430    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
431        return;
432    }
433#else
434    ZEND_PARSE_PARAMETERS_START(1, 1)
435        Z_PARAM_DOUBLE(num)
436    ZEND_PARSE_PARAMETERS_END();
437#endif
438    RETURN_DOUBLE(cos(num));
439}
440/* }}} */
441
442/* {{{ proto float tan(float number)
443   Returns the tangent of the number in radians */
444PHP_FUNCTION(tan)
445{
446    double num;
447
448#ifndef FAST_ZPP
449    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
450        return;
451    }
452#else
453    ZEND_PARSE_PARAMETERS_START(1, 1)
454        Z_PARAM_DOUBLE(num)
455    ZEND_PARSE_PARAMETERS_END();
456#endif
457    RETURN_DOUBLE(tan(num));
458}
459/* }}} */
460
461/* {{{ proto float asin(float number)
462   Returns the arc sine of the number in radians */
463PHP_FUNCTION(asin)
464{
465    double num;
466
467#ifndef FAST_ZPP
468    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
469        return;
470    }
471#else
472    ZEND_PARSE_PARAMETERS_START(1, 1)
473        Z_PARAM_DOUBLE(num)
474    ZEND_PARSE_PARAMETERS_END();
475#endif
476    RETURN_DOUBLE(asin(num));
477}
478/* }}} */
479
480/* {{{ proto float acos(float number)
481   Return the arc cosine of the number in radians */
482PHP_FUNCTION(acos)
483{
484    double num;
485
486#ifndef FAST_ZPP
487    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
488        return;
489    }
490#else
491    ZEND_PARSE_PARAMETERS_START(1, 1)
492        Z_PARAM_DOUBLE(num)
493    ZEND_PARSE_PARAMETERS_END();
494#endif
495    RETURN_DOUBLE(acos(num));
496}
497/* }}} */
498
499/* {{{ proto float atan(float number)
500   Returns the arc tangent of the number in radians */
501PHP_FUNCTION(atan)
502{
503    double num;
504
505#ifndef FAST_ZPP
506    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
507        return;
508    }
509#else
510    ZEND_PARSE_PARAMETERS_START(1, 1)
511        Z_PARAM_DOUBLE(num)
512    ZEND_PARSE_PARAMETERS_END();
513#endif
514    RETURN_DOUBLE(atan(num));
515}
516/* }}} */
517
518/* {{{ proto float atan2(float y, float x)
519   Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
520PHP_FUNCTION(atan2)
521{
522    double num1, num2;
523
524#ifndef FAST_ZPP
525    if (zend_parse_parameters(ZEND_NUM_ARGS(), "dd", &num1, &num2) == FAILURE) {
526        return;
527    }
528#else
529    ZEND_PARSE_PARAMETERS_START(2, 2)
530        Z_PARAM_DOUBLE(num1)
531        Z_PARAM_DOUBLE(num2)
532    ZEND_PARSE_PARAMETERS_END();
533#endif
534    RETURN_DOUBLE(atan2(num1, num2));
535}
536/* }}} */
537
538/* {{{ proto float sinh(float number)
539   Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
540PHP_FUNCTION(sinh)
541{
542    double num;
543
544#ifndef FAST_ZPP
545    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
546        return;
547    }
548#else
549    ZEND_PARSE_PARAMETERS_START(1, 1)
550        Z_PARAM_DOUBLE(num)
551    ZEND_PARSE_PARAMETERS_END();
552#endif
553    RETURN_DOUBLE(sinh(num));
554}
555/* }}} */
556
557/* {{{ proto float cosh(float number)
558   Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
559PHP_FUNCTION(cosh)
560{
561    double num;
562
563#ifndef FAST_ZPP
564    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
565        return;
566    }
567#else
568    ZEND_PARSE_PARAMETERS_START(1, 1)
569        Z_PARAM_DOUBLE(num)
570    ZEND_PARSE_PARAMETERS_END();
571#endif
572    RETURN_DOUBLE(cosh(num));
573}
574/* }}} */
575
576/* {{{ proto float tanh(float number)
577   Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
578PHP_FUNCTION(tanh)
579{
580    double num;
581
582#ifndef FAST_ZPP
583    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
584        return;
585    }
586#else
587    ZEND_PARSE_PARAMETERS_START(1, 1)
588        Z_PARAM_DOUBLE(num)
589    ZEND_PARSE_PARAMETERS_END();
590#endif
591    RETURN_DOUBLE(tanh(num));
592}
593/* }}} */
594
595/* {{{ proto float asinh(float number)
596   Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
597PHP_FUNCTION(asinh)
598{
599    double num;
600
601#ifndef FAST_ZPP
602    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
603        return;
604    }
605#else
606    ZEND_PARSE_PARAMETERS_START(1, 1)
607        Z_PARAM_DOUBLE(num)
608    ZEND_PARSE_PARAMETERS_END();
609#endif
610    RETURN_DOUBLE(php_asinh(num));
611}
612/* }}} */
613
614/* {{{ proto float acosh(float number)
615   Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
616PHP_FUNCTION(acosh)
617{
618    double num;
619
620#ifndef FAST_ZPP
621    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
622        return;
623    }
624#else
625    ZEND_PARSE_PARAMETERS_START(1, 1)
626        Z_PARAM_DOUBLE(num)
627    ZEND_PARSE_PARAMETERS_END();
628#endif
629    RETURN_DOUBLE(php_acosh(num));
630}
631/* }}} */
632
633/* {{{ proto float atanh(float number)
634   Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
635PHP_FUNCTION(atanh)
636{
637    double num;
638
639#ifndef FAST_ZPP
640    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
641        return;
642    }
643#else
644    ZEND_PARSE_PARAMETERS_START(1, 1)
645        Z_PARAM_DOUBLE(num)
646    ZEND_PARSE_PARAMETERS_END();
647#endif
648    RETURN_DOUBLE(php_atanh(num));
649}
650/* }}} */
651
652/* {{{ proto float pi(void)
653   Returns an approximation of pi */
654PHP_FUNCTION(pi)
655{
656    RETURN_DOUBLE(M_PI);
657}
658/* }}} */
659
660/* {{{ proto bool is_finite(float val)
661   Returns whether argument is finite */
662PHP_FUNCTION(is_finite)
663{
664    double dval;
665
666#ifndef FAST_ZPP
667    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &dval) == FAILURE) {
668        return;
669    }
670#else
671    ZEND_PARSE_PARAMETERS_START(1, 1)
672        Z_PARAM_DOUBLE(dval)
673    ZEND_PARSE_PARAMETERS_END();
674#endif
675    RETURN_BOOL(zend_finite(dval));
676}
677/* }}} */
678
679/* {{{ proto bool is_infinite(float val)
680   Returns whether argument is infinite */
681PHP_FUNCTION(is_infinite)
682{
683    double dval;
684
685#ifndef FAST_ZPP
686    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &dval) == FAILURE) {
687        return;
688    }
689#else
690    ZEND_PARSE_PARAMETERS_START(1, 1)
691        Z_PARAM_DOUBLE(dval)
692    ZEND_PARSE_PARAMETERS_END();
693#endif
694    RETURN_BOOL(zend_isinf(dval));
695}
696/* }}} */
697
698/* {{{ proto bool is_nan(float val)
699   Returns whether argument is not a number */
700PHP_FUNCTION(is_nan)
701{
702    double dval;
703
704#ifndef FAST_ZPP
705    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &dval) == FAILURE) {
706        return;
707    }
708#else
709    ZEND_PARSE_PARAMETERS_START(1, 1)
710        Z_PARAM_DOUBLE(dval)
711    ZEND_PARSE_PARAMETERS_END();
712#endif
713    RETURN_BOOL(zend_isnan(dval));
714}
715/* }}} */
716
717/* {{{ proto number pow(number base, number exponent)
718   Returns base raised to the power of exponent. Returns integer result when possible */
719PHP_FUNCTION(pow)
720{
721    zval *zbase, *zexp;
722
723    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/z/", &zbase, &zexp) == FAILURE) {
724        return;
725    }
726
727    pow_function(return_value, zbase, zexp);
728}
729/* }}} */
730
731/* {{{ proto float exp(float number)
732   Returns e raised to the power of the number */
733PHP_FUNCTION(exp)
734{
735    double num;
736
737#ifndef FAST_ZPP
738    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
739        return;
740    }
741#else
742    ZEND_PARSE_PARAMETERS_START(1, 1)
743        Z_PARAM_DOUBLE(num)
744    ZEND_PARSE_PARAMETERS_END();
745#endif
746
747    RETURN_DOUBLE(exp(num));
748}
749/* }}} */
750
751/* {{{ proto float expm1(float number)
752   Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
753/*
754   WARNING: this function is expermental: it could change its name or
755   disappear in the next version of PHP!
756*/
757PHP_FUNCTION(expm1)
758{
759    double num;
760
761#ifndef FAST_ZPP
762    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
763        return;
764    }
765#else
766    ZEND_PARSE_PARAMETERS_START(1, 1)
767        Z_PARAM_DOUBLE(num)
768    ZEND_PARSE_PARAMETERS_END();
769#endif
770
771    RETURN_DOUBLE(php_expm1(num));
772}
773/* }}} */
774
775/* {{{ proto float log1p(float number)
776   Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
777/*
778   WARNING: this function is expermental: it could change its name or
779   disappear in the next version of PHP!
780*/
781PHP_FUNCTION(log1p)
782{
783    double num;
784
785#ifndef FAST_ZPP
786    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
787        return;
788    }
789#else
790    ZEND_PARSE_PARAMETERS_START(1, 1)
791        Z_PARAM_DOUBLE(num)
792    ZEND_PARSE_PARAMETERS_END();
793#endif
794
795    RETURN_DOUBLE(php_log1p(num));
796}
797/* }}} */
798
799/* {{{ proto float log(float number, [float base])
800   Returns the natural logarithm of the number, or the base log if base is specified */
801PHP_FUNCTION(log)
802{
803    double num, base = 0;
804
805#ifndef FAST_ZPP
806    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d|d", &num, &base) == FAILURE) {
807        return;
808    }
809#else
810    ZEND_PARSE_PARAMETERS_START(1, 2)
811        Z_PARAM_DOUBLE(num)
812        Z_PARAM_OPTIONAL
813        Z_PARAM_DOUBLE(base)
814    ZEND_PARSE_PARAMETERS_END();
815#endif
816
817    if (ZEND_NUM_ARGS() == 1) {
818        RETURN_DOUBLE(log(num));
819    }
820
821#ifdef HAVE_LOG2
822    if (base == 2.0) {
823        RETURN_DOUBLE(log2(num));
824    }
825#endif
826
827    if (base == 10.0) {
828        RETURN_DOUBLE(log10(num));
829    }
830
831    if (base == 1.0) {
832        RETURN_DOUBLE(php_get_nan());
833    }
834
835    if (base <= 0.0) {
836        php_error_docref(NULL, E_WARNING, "base must be greater than 0");
837        RETURN_FALSE;
838    }
839
840    RETURN_DOUBLE(log(num) / log(base));
841}
842/* }}} */
843
844/* {{{ proto float log10(float number)
845   Returns the base-10 logarithm of the number */
846PHP_FUNCTION(log10)
847{
848    double num;
849
850#ifndef FAST_ZPP
851    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
852        return;
853    }
854#else
855    ZEND_PARSE_PARAMETERS_START(1, 1)
856        Z_PARAM_DOUBLE(num)
857    ZEND_PARSE_PARAMETERS_END();
858#endif
859
860    RETURN_DOUBLE(log10(num));
861}
862/* }}} */
863
864/* {{{ proto float sqrt(float number)
865   Returns the square root of the number */
866PHP_FUNCTION(sqrt)
867{
868    double num;
869
870#ifndef FAST_ZPP
871    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &num) == FAILURE) {
872        return;
873    }
874#else
875    ZEND_PARSE_PARAMETERS_START(1, 1)
876        Z_PARAM_DOUBLE(num)
877    ZEND_PARSE_PARAMETERS_END();
878#endif
879
880    RETURN_DOUBLE(sqrt(num));
881}
882/* }}} */
883
884/* {{{ proto float hypot(float num1, float num2)
885   Returns sqrt(num1*num1 + num2*num2) */
886PHP_FUNCTION(hypot)
887{
888    double num1, num2;
889
890#ifndef FAST_ZPP
891    if (zend_parse_parameters(ZEND_NUM_ARGS(), "dd", &num1, &num2) == FAILURE) {
892        return;
893    }
894#else
895    ZEND_PARSE_PARAMETERS_START(2, 2)
896        Z_PARAM_DOUBLE(num1)
897        Z_PARAM_DOUBLE(num2)
898    ZEND_PARSE_PARAMETERS_END();
899#endif
900
901#if HAVE_HYPOT
902    RETURN_DOUBLE(hypot(num1, num2));
903#elif defined(_MSC_VER)
904    RETURN_DOUBLE(_hypot(num1, num2));
905#else
906    RETURN_DOUBLE(sqrt((num1 * num1) + (num2 * num2)));
907#endif
908}
909/* }}} */
910
911/* {{{ proto float deg2rad(float number)
912   Converts the number in degrees to the radian equivalent */
913PHP_FUNCTION(deg2rad)
914{
915    double deg;
916
917#ifndef FAST_ZPP
918    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &deg) == FAILURE) {
919        return;
920    }
921#else
922    ZEND_PARSE_PARAMETERS_START(1, 1)
923        Z_PARAM_DOUBLE(deg)
924    ZEND_PARSE_PARAMETERS_END();
925#endif
926    RETURN_DOUBLE((deg / 180.0) * M_PI);
927}
928/* }}} */
929
930/* {{{ proto float rad2deg(float number)
931   Converts the radian number to the equivalent number in degrees */
932PHP_FUNCTION(rad2deg)
933{
934    double rad;
935
936#ifndef FAST_ZPP
937    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &rad) == FAILURE) {
938        return;
939    }
940#else
941    ZEND_PARSE_PARAMETERS_START(1, 1)
942        Z_PARAM_DOUBLE(rad)
943    ZEND_PARSE_PARAMETERS_END();
944#endif
945
946    RETURN_DOUBLE((rad / M_PI) * 180);
947}
948/* }}} */
949
950/* {{{ _php_math_basetolong */
951/*
952 * Convert a string representation of a base(2-36) number to a long.
953 */
954PHPAPI zend_long _php_math_basetolong(zval *arg, int base)
955{
956    zend_long num = 0, digit, onum;
957    zend_long i;
958    char c, *s;
959
960    if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
961        return 0;
962    }
963
964    s = Z_STRVAL_P(arg);
965
966    for (i = Z_STRLEN_P(arg); i > 0; i--) {
967        c = *s++;
968
969        digit = (c >= '0' && c <= '9') ? c - '0'
970            : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
971            : (c >= 'a' && c <= 'z') ? c - 'a' + 10
972            : base;
973
974        if (digit >= base) {
975            continue;
976        }
977
978        onum = num;
979        num = num * base + digit;
980        if (num > onum)
981            continue;
982
983        {
984
985            php_error_docref(NULL, E_WARNING, "Number '%s' is too big to fit in long", s);
986            return ZEND_LONG_MAX;
987        }
988    }
989
990    return num;
991}
992/* }}} */
993
994/* {{{ _php_math_basetozval */
995/*
996 * Convert a string representation of a base(2-36) number to a zval.
997 */
998PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
999{
1000    zend_long num = 0;
1001    double fnum = 0;
1002    zend_long i;
1003    int mode = 0;
1004    char c, *s;
1005    zend_long cutoff;
1006    int cutlim;
1007
1008    if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
1009        return FAILURE;
1010    }
1011
1012    s = Z_STRVAL_P(arg);
1013
1014    cutoff = ZEND_LONG_MAX / base;
1015    cutlim = ZEND_LONG_MAX % base;
1016
1017    for (i = Z_STRLEN_P(arg); i > 0; i--) {
1018        c = *s++;
1019
1020        /* might not work for EBCDIC */
1021        if (c >= '0' && c <= '9')
1022            c -= '0';
1023        else if (c >= 'A' && c <= 'Z')
1024            c -= 'A' - 10;
1025        else if (c >= 'a' && c <= 'z')
1026            c -= 'a' - 10;
1027        else
1028            continue;
1029
1030        if (c >= base)
1031            continue;
1032
1033        switch (mode) {
1034        case 0: /* Integer */
1035            if (num < cutoff || (num == cutoff && c <= cutlim)) {
1036                num = num * base + c;
1037                break;
1038            } else {
1039                fnum = (double)num;
1040                mode = 1;
1041            }
1042            /* fall-through */
1043        case 1: /* Float */
1044            fnum = fnum * base + c;
1045        }
1046    }
1047
1048    if (mode == 1) {
1049        ZVAL_DOUBLE(ret, fnum);
1050    } else {
1051        ZVAL_LONG(ret, num);
1052    }
1053    return SUCCESS;
1054}
1055/* }}} */
1056
1057/* {{{ _php_math_longtobase */
1058/*
1059 * Convert a long to a string containing a base(2-36) representation of
1060 * the number.
1061 */
1062PHPAPI zend_string * _php_math_longtobase(zval *arg, int base)
1063{
1064    static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
1065    char buf[(sizeof(zend_ulong) << 3) + 1];
1066    char *ptr, *end;
1067    zend_ulong value;
1068
1069    if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
1070        return STR_EMPTY_ALLOC();
1071    }
1072
1073    value = Z_LVAL_P(arg);
1074
1075    end = ptr = buf + sizeof(buf) - 1;
1076    *ptr = '\0';
1077
1078    do {
1079        *--ptr = digits[value % base];
1080        value /= base;
1081    } while (ptr > buf && value);
1082
1083    return zend_string_init(ptr, end - ptr, 0);
1084}
1085/* }}} */
1086
1087/* {{{ _php_math_zvaltobase */
1088/*
1089 * Convert a zval to a string containing a base(2-36) representation of
1090 * the number.
1091 */
1092PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base)
1093{
1094    static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
1095
1096    if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
1097        return STR_EMPTY_ALLOC();
1098    }
1099
1100    if (Z_TYPE_P(arg) == IS_DOUBLE) {
1101        double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
1102        char *ptr, *end;
1103        char buf[(sizeof(double) << 3) + 1];
1104
1105        /* Don't try to convert +/- infinity */
1106        if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
1107            php_error_docref(NULL, E_WARNING, "Number too large");
1108            return STR_EMPTY_ALLOC();
1109        }
1110
1111        end = ptr = buf + sizeof(buf) - 1;
1112        *ptr = '\0';
1113
1114        do {
1115            *--ptr = digits[(int) fmod(fvalue, base)];
1116            fvalue /= base;
1117        } while (ptr > buf && fabs(fvalue) >= 1);
1118
1119        return zend_string_init(ptr, end - ptr, 0);
1120    }
1121
1122    return _php_math_longtobase(arg, base);
1123}
1124/* }}} */
1125
1126/* {{{ proto int bindec(string binary_number)
1127   Returns the decimal equivalent of the binary number */
1128PHP_FUNCTION(bindec)
1129{
1130    zval *arg;
1131
1132    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1133        return;
1134    }
1135    convert_to_string_ex(arg);
1136    if (_php_math_basetozval(arg, 2, return_value) == FAILURE) {
1137        RETURN_FALSE;
1138    }
1139}
1140/* }}} */
1141
1142/* {{{ proto int hexdec(string hexadecimal_number)
1143   Returns the decimal equivalent of the hexadecimal number */
1144PHP_FUNCTION(hexdec)
1145{
1146    zval *arg;
1147
1148    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1149        return;
1150    }
1151    convert_to_string_ex(arg);
1152    if (_php_math_basetozval(arg, 16, return_value) == FAILURE) {
1153        RETURN_FALSE;
1154    }
1155}
1156/* }}} */
1157
1158/* {{{ proto int octdec(string octal_number)
1159   Returns the decimal equivalent of an octal string */
1160PHP_FUNCTION(octdec)
1161{
1162    zval *arg;
1163
1164    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1165        return;
1166    }
1167    convert_to_string_ex(arg);
1168    if (_php_math_basetozval(arg, 8, return_value) == FAILURE) {
1169        RETURN_FALSE;
1170    }
1171}
1172/* }}} */
1173
1174/* {{{ proto string decbin(int decimal_number)
1175   Returns a string containing a binary representation of the number */
1176PHP_FUNCTION(decbin)
1177{
1178    zval *arg;
1179    zend_string *result;
1180
1181    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1182        return;
1183    }
1184    convert_to_long_ex(arg);
1185    result = _php_math_longtobase(arg, 2);
1186    RETURN_STR(result);
1187}
1188/* }}} */
1189
1190/* {{{ proto string decoct(int decimal_number)
1191   Returns a string containing an octal representation of the given number */
1192PHP_FUNCTION(decoct)
1193{
1194    zval *arg;
1195    zend_string *result;
1196
1197    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1198        return;
1199    }
1200    convert_to_long_ex(arg);
1201    result = _php_math_longtobase(arg, 8);
1202    RETURN_STR(result);
1203}
1204/* }}} */
1205
1206/* {{{ proto string dechex(int decimal_number)
1207   Returns a string containing a hexadecimal representation of the given number */
1208PHP_FUNCTION(dechex)
1209{
1210    zval *arg;
1211    zend_string *result;
1212
1213    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1214        return;
1215    }
1216    convert_to_long_ex(arg);
1217    result = _php_math_longtobase(arg, 16);
1218    RETURN_STR(result);
1219}
1220/* }}} */
1221
1222/* {{{ proto string base_convert(string number, int frombase, int tobase)
1223   Converts a number in a string from any base <= 36 to any base <= 36 */
1224PHP_FUNCTION(base_convert)
1225{
1226    zval *number, temp;
1227    zend_long frombase, tobase;
1228    zend_string *result;
1229
1230    if (zend_parse_parameters(ZEND_NUM_ARGS(), "zll", &number, &frombase, &tobase) == FAILURE) {
1231        return;
1232    }
1233    convert_to_string_ex(number);
1234
1235    if (frombase < 2 || frombase > 36) {
1236        php_error_docref(NULL, E_WARNING, "Invalid `from base' (%pd)", frombase);
1237        RETURN_FALSE;
1238    }
1239    if (tobase < 2 || tobase > 36) {
1240        php_error_docref(NULL, E_WARNING, "Invalid `to base' (%pd)", tobase);
1241        RETURN_FALSE;
1242    }
1243
1244    if(_php_math_basetozval(number, (int)frombase, &temp) == FAILURE) {
1245        RETURN_FALSE;
1246    }
1247    result = _php_math_zvaltobase(&temp, (int)tobase);
1248    RETVAL_STR(result);
1249}
1250/* }}} */
1251
1252/* {{{ _php_math_number_format
1253*/
1254PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1255{
1256    return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1257}
1258
1259PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, char *dec_point,
1260        size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len)
1261{
1262    zend_string *res;
1263    zend_string *tmpbuf;
1264    char *s, *t;  /* source, target */
1265    char *dp;
1266    int integral;
1267    int reslen = 0;
1268    int count = 0;
1269    int is_negative=0;
1270
1271    if (d < 0) {
1272        is_negative = 1;
1273        d = -d;
1274    }
1275
1276    dec = MAX(0, dec);
1277    d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1278    tmpbuf = strpprintf(0, "%.*F", dec, d);
1279    if (tmpbuf == NULL) {
1280        return NULL;
1281    } else if (!isdigit((int)tmpbuf->val[0])) {
1282        return tmpbuf;
1283    }
1284
1285    /* find decimal point, if expected */
1286    if (dec) {
1287        dp = strpbrk(tmpbuf->val, ".,");
1288    } else {
1289        dp = NULL;
1290    }
1291
1292    /* calculate the length of the return buffer */
1293    if (dp) {
1294        integral = (int)(dp - tmpbuf->val);
1295    } else {
1296        /* no decimal point was found */
1297        integral = (int)tmpbuf->len;
1298    }
1299
1300    /* allow for thousand separators */
1301    if (thousand_sep) {
1302        integral += (int)(thousand_sep_len * ((integral-1) / 3));
1303    }
1304
1305    reslen = integral;
1306
1307    if (dec) {
1308        reslen += dec;
1309
1310        if (dec_point) {
1311            reslen += (int)dec_point_len;
1312        }
1313    }
1314
1315    /* add a byte for minus sign */
1316    if (is_negative) {
1317        reslen++;
1318    }
1319    res = zend_string_alloc(reslen, 0);
1320
1321    s = tmpbuf->val + tmpbuf->len - 1;
1322    t = res->val + reslen;
1323    *t-- = '\0';
1324
1325    /* copy the decimal places.
1326     * Take care, as the sprintf implementation may return less places than
1327     * we requested due to internal buffer limitations */
1328    if (dec) {
1329        int declen = (int)(dp ? s - dp : 0);
1330        int topad = dec > declen ? dec - declen : 0;
1331
1332        /* pad with '0's */
1333        while (topad--) {
1334            *t-- = '0';
1335        }
1336
1337        if (dp) {
1338            s -= declen + 1; /* +1 to skip the point */
1339            t -= declen;
1340
1341            /* now copy the chars after the point */
1342            memcpy(t + 1, dp + 1, declen);
1343        }
1344
1345        /* add decimal point */
1346        if (dec_point) {
1347            t -= dec_point_len;
1348            memcpy(t + 1, dec_point, dec_point_len);
1349        }
1350    }
1351
1352    /* copy the numbers before the decimal point, adding thousand
1353     * separator every three digits */
1354    while (s >= tmpbuf->val) {
1355        *t-- = *s--;
1356        if (thousand_sep && (++count%3)==0 && s>=tmpbuf->val) {
1357            t -= thousand_sep_len;
1358            memcpy(t + 1, thousand_sep, thousand_sep_len);
1359        }
1360    }
1361
1362    /* and a minus sign, if needed */
1363    if (is_negative) {
1364        *t-- = '-';
1365    }
1366
1367    res->len = reslen;
1368    zend_string_release(tmpbuf);
1369    return res;
1370}
1371
1372/* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_separator, string thousands_separator]])
1373   Formats a number with grouped thousands */
1374PHP_FUNCTION(number_format)
1375{
1376    double num;
1377    zend_long dec = 0;
1378    char *thousand_sep = NULL, *dec_point = NULL;
1379    char thousand_sep_chr = ',', dec_point_chr = '.';
1380    size_t thousand_sep_len = 0, dec_point_len = 0;
1381
1382#ifndef FAST_ZPP
1383    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d|ls!s!", &num, &dec, &dec_point, &dec_point_len, &thousand_sep, &thousand_sep_len) == FAILURE) {
1384        return;
1385    }
1386#else
1387    ZEND_PARSE_PARAMETERS_START(1, 4)
1388        Z_PARAM_DOUBLE(num)
1389        Z_PARAM_OPTIONAL
1390        Z_PARAM_LONG(dec)
1391        Z_PARAM_STRING_EX(dec_point, dec_point_len, 1, 0)
1392        Z_PARAM_STRING_EX(thousand_sep, thousand_sep_len, 1, 0)
1393    ZEND_PARSE_PARAMETERS_END();
1394#endif
1395
1396    switch(ZEND_NUM_ARGS()) {
1397    case 1:
1398        RETURN_STR(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr));
1399        break;
1400    case 2:
1401        RETURN_STR(_php_math_number_format(num, (int)dec, dec_point_chr, thousand_sep_chr));
1402        break;
1403    case 4:
1404        if (dec_point == NULL) {
1405            dec_point = &dec_point_chr;
1406            dec_point_len = 1;
1407        }
1408
1409        if (thousand_sep == NULL) {
1410            thousand_sep = &thousand_sep_chr;
1411            thousand_sep_len = 1;
1412        }
1413
1414        RETVAL_STR(_php_math_number_format_ex(num, (int)dec,
1415                dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1416        break;
1417    default:
1418        WRONG_PARAM_COUNT;
1419        break;
1420    }
1421}
1422/* }}} */
1423
1424/* {{{ proto float fmod(float x, float y)
1425   Returns the remainder of dividing x by y as a float */
1426PHP_FUNCTION(fmod)
1427{
1428    double num1, num2;
1429
1430#ifndef FAST_ZPP
1431    if (zend_parse_parameters(ZEND_NUM_ARGS(), "dd",  &num1, &num2) == FAILURE) {
1432        return;
1433    }
1434#else
1435    ZEND_PARSE_PARAMETERS_START(2, 2)
1436        Z_PARAM_DOUBLE(num1)
1437        Z_PARAM_DOUBLE(num2)
1438    ZEND_PARSE_PARAMETERS_END();
1439#endif
1440
1441    RETURN_DOUBLE(fmod(num1, num2));
1442}
1443/* }}} */
1444
1445/* {{{ proto int intdiv(int numerator, int divisor)
1446   Returns the integer division of the numerator by the divisor */
1447PHP_FUNCTION(intdiv)
1448{
1449    zend_long numerator, divisor;
1450
1451    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &numerator, &divisor) == FAILURE) {
1452        return;
1453    }
1454
1455    if (divisor == 0) {
1456        zend_throw_exception_ex(NULL, 0, "Division by zero");
1457        return;
1458    } else if (divisor == -1 && numerator == ZEND_LONG_MIN) {
1459        /* Prevent overflow error/crash
1460           We don't return a float here as that violates function contract */
1461        zend_throw_exception_ex(NULL, 0, "Division of PHP_INT_MIN by -1 is not an integer");
1462        return;
1463    }
1464
1465    RETURN_LONG(numerator/divisor);
1466}
1467/* }}} */
1468
1469/*
1470 * Local variables:
1471 * tab-width: 4
1472 * c-basic-offset: 4
1473 * End:
1474 * vim600: fdm=marker
1475 * vim: noet sw=4 ts=4
1476 */
1477