1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 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: math.c 272374 2008-12-31 11:17:49Z sebastian $ */
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 : /*
33 : * Pertains to some of the code found in the php_round() function
34 : * Ref: http://www.freebsd.org/cgi/query-pr.cgi?pr=59797
35 : *
36 : * Copyright (c) 2003, Steven G. Kargl
37 : * All rights reserved.
38 : *
39 : * Redistribution and use in source and binary forms, with or without
40 : * modification, are permitted provided that the following conditions
41 : * are met:
42 : * 1. Redistributions of source code must retain the above copyright
43 : * notice unmodified, this list of conditions, and the following
44 : * disclaimer.
45 : * 2. Redistributions in binary form must reproduce the above copyright
46 : * notice, this list of conditions and the following disclaimer in the
47 : * documentation and/or other materials provided with the distribution.
48 : *
49 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
50 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
51 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
52 : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
53 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
54 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
58 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 : */
60 200 : static double php_round(double val, int places) {
61 : double t;
62 200 : double f = pow(10.0, (double) places);
63 200 : double x = val * f;
64 :
65 200 : if (zend_isinf(x) || zend_isnan(x)) {
66 6 : return val;
67 : }
68 :
69 194 : if (x >= 0.0) {
70 163 : t = ceil(x);
71 163 : if ((t - x) > 0.50000000001) {
72 36 : t -= 1.0;
73 : }
74 : } else {
75 31 : t = ceil(-x);
76 31 : if ((t + x) > 0.50000000001) {
77 4 : t -= 1.0;
78 : }
79 31 : t = -t;
80 : }
81 194 : x = t / f;
82 :
83 194 : return !zend_isnan(x) ? x : t;
84 : }
85 :
86 : /* {{{ proto int abs(int number)
87 : Return the absolute value of the number */
88 : PHP_FUNCTION(abs)
89 69 : {
90 : zval **value;
91 :
92 69 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &value) == FAILURE) {
93 2 : WRONG_PARAM_COUNT;
94 : }
95 :
96 67 : convert_scalar_to_number_ex(value);
97 :
98 67 : if (Z_TYPE_PP(value) == IS_DOUBLE) {
99 38 : RETURN_DOUBLE(fabs(Z_DVAL_PP(value)));
100 29 : } else if (Z_TYPE_PP(value) == IS_LONG) {
101 28 : if (Z_LVAL_PP(value) == LONG_MIN) {
102 1 : RETURN_DOUBLE(-(double)LONG_MIN);
103 : } else {
104 27 : RETURN_LONG(Z_LVAL_PP(value) < 0 ? -Z_LVAL_PP(value) : Z_LVAL_PP(value));
105 : }
106 : }
107 1 : RETURN_FALSE;
108 : }
109 : /* }}} */
110 :
111 : /* {{{ proto float ceil(float number)
112 : Returns the next highest integer value of the number */
113 : PHP_FUNCTION(ceil)
114 56 : {
115 : zval **value;
116 :
117 56 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &value) == FAILURE) {
118 2 : WRONG_PARAM_COUNT;
119 : }
120 :
121 54 : convert_scalar_to_number_ex(value);
122 :
123 54 : if (Z_TYPE_PP(value) == IS_DOUBLE) {
124 23 : RETURN_DOUBLE(ceil(Z_DVAL_PP(value)));
125 31 : } else if (Z_TYPE_PP(value) == IS_LONG) {
126 30 : convert_to_double_ex(value);
127 30 : RETURN_DOUBLE(Z_DVAL_PP(value));
128 : }
129 :
130 1 : RETURN_FALSE;
131 : }
132 : /* }}} */
133 :
134 : /* {{{ proto float floor(float number)
135 : Returns the next lowest integer value from the number */
136 : PHP_FUNCTION(floor)
137 58 : {
138 : zval **value;
139 :
140 58 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &value) == FAILURE) {
141 2 : WRONG_PARAM_COUNT;
142 : }
143 :
144 56 : convert_scalar_to_number_ex(value);
145 :
146 56 : if (Z_TYPE_PP(value) == IS_DOUBLE) {
147 24 : RETURN_DOUBLE(floor(Z_DVAL_PP(value)));
148 32 : } else if (Z_TYPE_PP(value) == IS_LONG) {
149 31 : convert_to_double_ex(value);
150 31 : RETURN_DOUBLE(Z_DVAL_PP(value));
151 : }
152 :
153 1 : RETURN_FALSE;
154 : }
155 : /* }}} */
156 :
157 : /* {{{ proto float round(float number [, int precision])
158 : Returns the number rounded to specified precision */
159 : PHP_FUNCTION(round)
160 207 : {
161 : zval **value, **precision;
162 207 : int places = 0;
163 : double return_val;
164 :
165 207 : if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 2 ||
166 : zend_get_parameters_ex(ZEND_NUM_ARGS(), &value, &precision) == FAILURE) {
167 2 : WRONG_PARAM_COUNT;
168 : }
169 :
170 205 : if (ZEND_NUM_ARGS() == 2) {
171 197 : convert_to_long_ex(precision);
172 197 : places = (int) Z_LVAL_PP(precision);
173 : }
174 :
175 205 : convert_scalar_to_number_ex(value);
176 :
177 205 : switch (Z_TYPE_PP(value)) {
178 : case IS_LONG:
179 : /* Simple case - long that doesn't need to be rounded. */
180 72 : if (places >= 0) {
181 72 : RETURN_DOUBLE((double) Z_LVAL_PP(value));
182 : }
183 : /* break omitted intentionally */
184 :
185 : case IS_DOUBLE:
186 132 : return_val = (Z_TYPE_PP(value) == IS_LONG) ?
187 : (double)Z_LVAL_PP(value) : Z_DVAL_PP(value);
188 :
189 132 : return_val = php_round(return_val, places);
190 :
191 132 : RETURN_DOUBLE(return_val);
192 : break;
193 :
194 : default:
195 1 : RETURN_FALSE;
196 : break;
197 : }
198 : }
199 : /* }}} */
200 :
201 : /* {{{ proto float sin(float number)
202 : Returns the sine of the number in radians */
203 : PHP_FUNCTION(sin)
204 30 : {
205 : zval **num;
206 :
207 30 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
208 2 : WRONG_PARAM_COUNT;
209 : }
210 28 : convert_to_double_ex(num);
211 28 : Z_DVAL_P(return_value) = sin(Z_DVAL_PP(num));
212 28 : Z_TYPE_P(return_value) = IS_DOUBLE;
213 : }
214 : /* }}} */
215 :
216 : /* {{{ proto float cos(float number)
217 : Returns the cosine of the number in radians */
218 : PHP_FUNCTION(cos)
219 32 : {
220 : zval **num;
221 :
222 32 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
223 2 : WRONG_PARAM_COUNT;
224 : }
225 30 : convert_to_double_ex(num);
226 30 : Z_DVAL_P(return_value) = cos(Z_DVAL_PP(num));
227 30 : Z_TYPE_P(return_value) = IS_DOUBLE;
228 : }
229 : /* }}} */
230 :
231 : /* {{{ proto float tan(float number)
232 : Returns the tangent of the number in radians */
233 : PHP_FUNCTION(tan)
234 22 : {
235 : zval **num;
236 :
237 22 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
238 2 : WRONG_PARAM_COUNT;
239 : }
240 20 : convert_to_double_ex(num);
241 20 : Z_DVAL_P(return_value) = tan(Z_DVAL_PP(num));
242 20 : Z_TYPE_P(return_value) = IS_DOUBLE;
243 : }
244 : /* }}} */
245 :
246 : /* {{{ proto float asin(float number)
247 : Returns the arc sine of the number in radians */
248 : PHP_FUNCTION(asin)
249 21 : {
250 : zval **num;
251 :
252 21 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
253 2 : WRONG_PARAM_COUNT;
254 : }
255 19 : convert_to_double_ex(num);
256 19 : Z_DVAL_P(return_value) = asin(Z_DVAL_PP(num));
257 19 : Z_TYPE_P(return_value) = IS_DOUBLE;
258 : }
259 : /* }}} */
260 :
261 : /* {{{ proto float acos(float number)
262 : Return the arc cosine of the number in radians */
263 : PHP_FUNCTION(acos)
264 26 : {
265 : zval **num;
266 :
267 26 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
268 2 : WRONG_PARAM_COUNT;
269 : }
270 24 : convert_to_double_ex(num);
271 24 : Z_DVAL_P(return_value) = acos(Z_DVAL_PP(num));
272 24 : Z_TYPE_P(return_value) = IS_DOUBLE;
273 : }
274 : /* }}} */
275 :
276 : /* {{{ proto float atan(float number)
277 : Returns the arc tangent of the number in radians */
278 : PHP_FUNCTION(atan)
279 19 : {
280 : zval **num;
281 :
282 19 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
283 2 : WRONG_PARAM_COUNT;
284 : }
285 17 : convert_to_double_ex(num);
286 17 : Z_DVAL_P(return_value) = atan(Z_DVAL_PP(num));
287 17 : Z_TYPE_P(return_value) = IS_DOUBLE;
288 : }
289 : /* }}} */
290 :
291 : /* {{{ proto float atan2(float y, float x)
292 : Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
293 : PHP_FUNCTION(atan2)
294 199 : {
295 : zval **num1, **num2;
296 :
297 199 : if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &num1, &num2) == FAILURE) {
298 3 : WRONG_PARAM_COUNT;
299 : }
300 196 : convert_to_double_ex(num1);
301 196 : convert_to_double_ex(num2);
302 196 : Z_DVAL_P(return_value) = atan2(Z_DVAL_PP(num1), Z_DVAL_PP(num2));
303 196 : Z_TYPE_P(return_value) = IS_DOUBLE;
304 : }
305 : /* }}} */
306 :
307 : /* {{{ proto float sinh(float number)
308 : Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
309 : PHP_FUNCTION(sinh)
310 25 : {
311 : zval **num;
312 :
313 25 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
314 2 : WRONG_PARAM_COUNT;
315 : }
316 23 : convert_to_double_ex(num);
317 23 : Z_DVAL_P(return_value) = sinh(Z_DVAL_PP(num));
318 23 : Z_TYPE_P(return_value) = IS_DOUBLE;
319 : }
320 : /* }}} */
321 :
322 : /* {{{ proto float cosh(float number)
323 : Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
324 : PHP_FUNCTION(cosh)
325 25 : {
326 : zval **num;
327 :
328 25 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
329 2 : WRONG_PARAM_COUNT;
330 : }
331 23 : convert_to_double_ex(num);
332 23 : Z_DVAL_P(return_value) = cosh(Z_DVAL_PP(num));
333 23 : Z_TYPE_P(return_value) = IS_DOUBLE;
334 : }
335 : /* }}} */
336 :
337 : /* {{{ proto float tanh(float number)
338 : Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
339 : PHP_FUNCTION(tanh)
340 25 : {
341 : zval **num;
342 :
343 25 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
344 2 : WRONG_PARAM_COUNT;
345 : }
346 23 : convert_to_double_ex(num);
347 23 : Z_DVAL_P(return_value) = tanh(Z_DVAL_PP(num));
348 23 : Z_TYPE_P(return_value) = IS_DOUBLE;
349 : }
350 : /* }}} */
351 :
352 : #if !defined(PHP_WIN32) && !defined(NETWARE)
353 : #ifdef HAVE_ASINH
354 : /* {{{ proto float asinh(float number)
355 : Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
356 : PHP_FUNCTION(asinh)
357 21 : {
358 : zval **num;
359 :
360 21 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
361 2 : WRONG_PARAM_COUNT;
362 : }
363 19 : convert_to_double_ex(num);
364 19 : Z_DVAL_P(return_value) = asinh(Z_DVAL_PP(num));
365 19 : Z_TYPE_P(return_value) = IS_DOUBLE;
366 : }
367 : /* }}} */
368 : #endif /* HAVE_ASINH */
369 :
370 : #ifdef HAVE_ACOSH
371 : /* {{{ proto float acosh(float number)
372 : Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
373 : PHP_FUNCTION(acosh)
374 21 : {
375 : zval **num;
376 :
377 21 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
378 2 : WRONG_PARAM_COUNT;
379 : }
380 19 : convert_to_double_ex(num);
381 19 : Z_DVAL_P(return_value) = acosh(Z_DVAL_PP(num));
382 19 : Z_TYPE_P(return_value) = IS_DOUBLE;
383 : }
384 : /* }}} */
385 : #endif /* HAVE_ACOSH */
386 :
387 : #ifdef HAVE_ATANH
388 : /* {{{ proto float atanh(float number)
389 : Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
390 : PHP_FUNCTION(atanh)
391 21 : {
392 : zval **num;
393 :
394 21 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
395 2 : WRONG_PARAM_COUNT;
396 : }
397 19 : convert_to_double_ex(num);
398 19 : Z_DVAL_P(return_value) = atanh(Z_DVAL_PP(num));
399 19 : Z_TYPE_P(return_value) = IS_DOUBLE;
400 : }
401 : /* }}} */
402 : #endif /* HAVE_ATANH */
403 : #endif /* !defined(PHP_WIN32) && !defined(NETWARE) */
404 :
405 : /* {{{ proto float pi(void)
406 : Returns an approximation of pi */
407 : PHP_FUNCTION(pi)
408 1 : {
409 1 : Z_DVAL_P(return_value) = M_PI;
410 1 : Z_TYPE_P(return_value) = IS_DOUBLE;
411 1 : }
412 : /* }}} */
413 :
414 : /* {{{ proto bool is_finite(float val)
415 : Returns whether argument is finite */
416 : PHP_FUNCTION(is_finite)
417 43 : {
418 : double dval;
419 :
420 :
421 43 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
422 10 : return;
423 : }
424 33 : RETURN_BOOL(zend_finite(dval));
425 : }
426 : /* }}} */
427 :
428 : /* {{{ proto bool is_infinite(float val)
429 : Returns whether argument is infinite */
430 : PHP_FUNCTION(is_infinite)
431 51 : {
432 : double dval;
433 :
434 51 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
435 10 : return;
436 : }
437 41 : RETURN_BOOL(zend_isinf(dval));
438 : }
439 : /* }}} */
440 :
441 : /* {{{ proto bool is_nan(float val)
442 : Returns whether argument is not a number */
443 : PHP_FUNCTION(is_nan)
444 45 : {
445 : double dval;
446 :
447 45 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
448 10 : return;
449 : }
450 35 : RETURN_BOOL(zend_isnan(dval));
451 : }
452 : /* }}} */
453 :
454 : /* {{{ proto number pow(number base, number exponent)
455 : Returns base raised to the power of exponent. Returns integer result when possible */
456 : PHP_FUNCTION(pow)
457 853 : {
458 : zval *zbase, *zexp;
459 :
460 853 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/", &zbase, &zexp) == FAILURE) {
461 6 : return;
462 : }
463 :
464 : /* make sure we're dealing with numbers */
465 847 : convert_scalar_to_number(zbase TSRMLS_CC);
466 847 : convert_scalar_to_number(zexp TSRMLS_CC);
467 :
468 : /* if both base and exponent were longs, we'll try to get a long out */
469 847 : if (Z_TYPE_P(zbase) == IS_LONG && Z_TYPE_P(zexp) == IS_LONG && Z_LVAL_P(zexp) >= 0) {
470 106 : long l1 = 1, l2 = Z_LVAL_P(zbase), i = Z_LVAL_P(zexp);
471 :
472 106 : if (i == 0) {
473 13 : RETURN_LONG(1L);
474 93 : } else if (l2 == 0) {
475 14 : RETURN_LONG(0);
476 : }
477 :
478 : /* calculate pow(long,long) in O(log exp) operations, bail if overflow */
479 382 : while (i >= 1) {
480 : int overflow;
481 303 : double dval = 0.0;
482 :
483 303 : if (i % 2) {
484 139 : --i;
485 139 : ZEND_SIGNED_MULTIPLY_LONG(l1,l2,l1,dval,overflow);
486 139 : if (overflow) RETURN_DOUBLE(dval * pow(l2,i));
487 : } else {
488 164 : i /= 2;
489 164 : ZEND_SIGNED_MULTIPLY_LONG(l2,l2,l2,dval,overflow);
490 164 : if (overflow) RETURN_DOUBLE((double)l1 * pow(dval,i));
491 : }
492 274 : if (i == 0) {
493 50 : RETURN_LONG(l1);
494 : }
495 : }
496 : }
497 741 : convert_to_double(zbase);
498 741 : convert_to_double(zexp);
499 :
500 741 : RETURN_DOUBLE( pow(Z_DVAL_P(zbase),Z_DVAL_P(zexp)) );
501 : }
502 : /* }}} */
503 :
504 : /* {{{ proto float exp(float number)
505 : Returns e raised to the power of the number */
506 : PHP_FUNCTION(exp)
507 241 : {
508 : double num;
509 :
510 241 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
511 10 : return;
512 : }
513 :
514 231 : RETURN_DOUBLE(exp(num));
515 : }
516 : /* }}} */
517 :
518 : #if !defined(PHP_WIN32) && !defined(NETWARE)
519 : /* {{{ proto float expm1(float number)
520 : Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
521 : /*
522 : WARNING: this function is expermental: it could change its name or
523 : disappear in the next version of PHP!
524 : */
525 : PHP_FUNCTION(expm1)
526 42 : {
527 : zval **num;
528 :
529 42 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
530 2 : WRONG_PARAM_COUNT;
531 : }
532 40 : convert_to_double_ex(num);
533 40 : Z_DVAL_P(return_value) = expm1(Z_DVAL_PP(num));
534 40 : Z_TYPE_P(return_value) = IS_DOUBLE;
535 : }
536 : /* }}} */
537 :
538 : #ifdef HAVE_LOG1P
539 : /* {{{ proto float log1p(float number)
540 : Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
541 : /*
542 : WARNING: this function is expermental: it could change its name or
543 : disappear in the next version of PHP!
544 : */
545 : PHP_FUNCTION(log1p)
546 41 : {
547 : zval **num;
548 :
549 41 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
550 2 : WRONG_PARAM_COUNT;
551 : }
552 39 : convert_to_double_ex(num);
553 39 : Z_DVAL_P(return_value) = log1p(Z_DVAL_PP(num));
554 39 : Z_TYPE_P(return_value) = IS_DOUBLE;
555 : }
556 : /* }}} */
557 : #endif /* HAVE_LOG1P */
558 : #endif /* !defined(PHP_WIN32) && !defined(NETWARE) */
559 :
560 : /* {{{ proto float log(float number, [float base])
561 : Returns the natural logarithm of the number, or the base log if base is specified */
562 : PHP_FUNCTION(log)
563 731 : {
564 : zval **num, **base;
565 :
566 731 : switch (ZEND_NUM_ARGS()) {
567 : case 1:
568 214 : if (zend_get_parameters_ex(1, &num) == FAILURE) {
569 0 : WRONG_PARAM_COUNT;
570 : }
571 214 : convert_to_double_ex(num);
572 214 : RETURN_DOUBLE(log(Z_DVAL_PP(num)));
573 : case 2:
574 515 : if (zend_get_parameters_ex(2, &num, &base) == FAILURE) {
575 0 : WRONG_PARAM_COUNT;
576 : }
577 515 : convert_to_double_ex(num);
578 515 : convert_to_double_ex(base);
579 :
580 515 : if (Z_DVAL_PP(base) <= 0.0) {
581 16 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");
582 16 : RETURN_FALSE;
583 : }
584 499 : RETURN_DOUBLE(log(Z_DVAL_PP(num)) / log(Z_DVAL_PP(base)));
585 : default:
586 2 : WRONG_PARAM_COUNT;
587 : }
588 : }
589 : /* }}} */
590 :
591 : /* {{{ proto float log10(float number)
592 : Returns the base-10 logarithm of the number */
593 : PHP_FUNCTION(log10)
594 24 : {
595 : zval **num;
596 :
597 24 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
598 2 : WRONG_PARAM_COUNT;
599 : }
600 22 : convert_to_double_ex(num);
601 22 : Z_DVAL_P(return_value) = log10(Z_DVAL_PP(num));
602 22 : Z_TYPE_P(return_value) = IS_DOUBLE;
603 : }
604 : /* }}} */
605 :
606 : /* {{{ proto float sqrt(float number)
607 : Returns the square root of the number */
608 : PHP_FUNCTION(sqrt)
609 33 : {
610 : zval **num;
611 :
612 33 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
613 2 : WRONG_PARAM_COUNT;
614 : }
615 31 : convert_to_double_ex(num);
616 31 : Z_DVAL_P(return_value) = sqrt(Z_DVAL_PP(num));
617 31 : Z_TYPE_P(return_value) = IS_DOUBLE;
618 : }
619 : /* }}} */
620 :
621 : /* {{{ proto float hypot(float num1, float num2)
622 : Returns sqrt(num1*num1 + num2*num2) */
623 : PHP_FUNCTION(hypot)
624 224 : {
625 : zval **num1, **num2;
626 :
627 224 : if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &num1, &num2) == FAILURE) {
628 3 : WRONG_PARAM_COUNT;
629 : }
630 221 : convert_to_double_ex(num1);
631 221 : convert_to_double_ex(num2);
632 : #if HAVE_HYPOT
633 221 : Z_DVAL_P(return_value) = hypot(Z_DVAL_PP(num1), Z_DVAL_PP(num2));
634 : #elif defined(_MSC_VER)
635 : Z_DVAL_P(return_value) = _hypot(Z_DVAL_PP(num1), Z_DVAL_PP(num2));
636 : #else
637 : Z_DVAL_P(return_value) = sqrt((Z_DVAL_PP(num1) * Z_DVAL_PP(num1)) +
638 : (Z_DVAL_PP(num2) * Z_DVAL_PP(num2)));
639 : #endif
640 221 : Z_TYPE_P(return_value) = IS_DOUBLE;
641 : }
642 : /* }}} */
643 :
644 : /* {{{ proto float deg2rad(float number)
645 : Converts the number in degrees to the radian equivalent */
646 : PHP_FUNCTION(deg2rad)
647 24 : {
648 : zval **deg;
649 :
650 24 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, °) == FAILURE) {
651 2 : WRONG_PARAM_COUNT;
652 : }
653 22 : convert_to_double_ex(deg);
654 22 : RETVAL_DOUBLE((Z_DVAL_PP(deg) / 180.0) * M_PI);
655 : }
656 : /* }}} */
657 :
658 : /* {{{ proto float rad2deg(float number)
659 : Converts the radian number to the equivalent number in degrees */
660 : PHP_FUNCTION(rad2deg)
661 21 : {
662 : zval **rad;
663 :
664 21 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &rad) == FAILURE) {
665 2 : WRONG_PARAM_COUNT;
666 : }
667 19 : convert_to_double_ex(rad);
668 19 : RETVAL_DOUBLE((Z_DVAL_PP(rad) / M_PI) * 180);
669 : }
670 : /* }}} */
671 :
672 : /* {{{ _php_math_basetolong */
673 : /*
674 : * Convert a string representation of a base(2-36) number to a long.
675 : */
676 : PHPAPI long _php_math_basetolong(zval *arg, int base)
677 0 : {
678 0 : long num = 0, digit, onum;
679 : int i;
680 : char c, *s;
681 :
682 0 : if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
683 0 : return 0;
684 : }
685 :
686 0 : s = Z_STRVAL_P(arg);
687 :
688 0 : for (i = Z_STRLEN_P(arg); i > 0; i--) {
689 0 : c = *s++;
690 :
691 0 : digit = (c >= '0' && c <= '9') ? c - '0'
692 : : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
693 : : (c >= 'a' && c <= 'z') ? c - 'a' + 10
694 : : base;
695 :
696 0 : if (digit >= base) {
697 0 : continue;
698 : }
699 :
700 0 : onum = num;
701 0 : num = num * base + digit;
702 0 : if (num > onum)
703 0 : continue;
704 :
705 : {
706 : TSRMLS_FETCH();
707 :
708 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
709 0 : return LONG_MAX;
710 : }
711 : }
712 :
713 0 : return num;
714 : }
715 : /* }}} */
716 :
717 : /* {{{ _php_math_basetozval */
718 : /*
719 : * Convert a string representation of a base(2-36) number to a zval.
720 : */
721 : PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
722 414 : {
723 414 : long num = 0;
724 414 : double fnum = 0;
725 : int i;
726 414 : int mode = 0;
727 : char c, *s;
728 : long cutoff;
729 : int cutlim;
730 :
731 414 : if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
732 0 : return FAILURE;
733 : }
734 :
735 414 : s = Z_STRVAL_P(arg);
736 :
737 414 : cutoff = LONG_MAX / base;
738 414 : cutlim = LONG_MAX % base;
739 :
740 1792 : for (i = Z_STRLEN_P(arg); i > 0; i--) {
741 1378 : c = *s++;
742 :
743 : /* might not work for EBCDIC */
744 2421 : if (c >= '0' && c <= '9')
745 1043 : c -= '0';
746 467 : else if (c >= 'A' && c <= 'Z')
747 132 : c -= 'A' - 10;
748 203 : else if (c >= 'a' && c <= 'z')
749 149 : c -= 'a' - 10;
750 : else
751 : continue;
752 :
753 1324 : if (c >= base)
754 368 : continue;
755 :
756 956 : switch (mode) {
757 : case 0: /* Integer */
758 946 : if (num < cutoff || (num == cutoff && c <= cutlim)) {
759 935 : num = num * base + c;
760 935 : break;
761 : } else {
762 11 : fnum = num;
763 11 : mode = 1;
764 : }
765 : /* fall-through */
766 : case 1: /* Float */
767 21 : fnum = fnum * base + c;
768 : }
769 : }
770 :
771 414 : if (mode == 1) {
772 11 : ZVAL_DOUBLE(ret, fnum);
773 : } else {
774 403 : ZVAL_LONG(ret, num);
775 : }
776 414 : return SUCCESS;
777 : }
778 : /* }}} */
779 :
780 : /* {{{ _php_math_longtobase */
781 : /*
782 : * Convert a long to a string containing a base(2-36) representation of
783 : * the number.
784 : */
785 : PHPAPI char * _php_math_longtobase(zval *arg, int base)
786 1430 : {
787 : static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
788 : char buf[(sizeof(unsigned long) << 3) + 1];
789 : char *ptr, *end;
790 : unsigned long value;
791 :
792 1430 : if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
793 0 : return STR_EMPTY_ALLOC();
794 : }
795 :
796 1430 : value = Z_LVAL_P(arg);
797 :
798 1430 : end = ptr = buf + sizeof(buf) - 1;
799 1430 : *ptr = '\0';
800 :
801 : do {
802 8244 : *--ptr = digits[value % base];
803 8244 : value /= base;
804 8244 : } while (ptr > buf && value);
805 :
806 1430 : return estrndup(ptr, end - ptr);
807 : }
808 : /* }}} */
809 :
810 : /* {{{ _php_math_zvaltobase */
811 : /*
812 : * Convert a zval to a string containing a base(2-36) representation of
813 : * the number.
814 : */
815 : PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
816 277 : {
817 : static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
818 :
819 277 : if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
820 0 : return STR_EMPTY_ALLOC();
821 : }
822 :
823 277 : if (Z_TYPE_P(arg) == IS_DOUBLE) {
824 0 : double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
825 : char *ptr, *end;
826 : char buf[(sizeof(double) << 3) + 1];
827 :
828 : /* Don't try to convert +/- infinity */
829 0 : if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
830 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
831 0 : return STR_EMPTY_ALLOC();
832 : }
833 :
834 0 : end = ptr = buf + sizeof(buf) - 1;
835 0 : *ptr = '\0';
836 :
837 : do {
838 0 : *--ptr = digits[(int) fmod(fvalue, base)];
839 0 : fvalue /= base;
840 0 : } while (ptr > buf && fabs(fvalue) >= 1);
841 :
842 0 : return estrndup(ptr, end - ptr);
843 : }
844 :
845 277 : return _php_math_longtobase(arg, base);
846 : }
847 : /* }}} */
848 :
849 : /* {{{ proto int bindec(string binary_number)
850 : Returns the decimal equivalent of the binary number */
851 : PHP_FUNCTION(bindec)
852 46 : {
853 : zval **arg;
854 :
855 46 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
856 2 : WRONG_PARAM_COUNT;
857 : }
858 :
859 44 : convert_to_string_ex(arg);
860 43 : if(_php_math_basetozval(*arg, 2, return_value) != SUCCESS) {
861 0 : RETURN_FALSE;
862 : }
863 : }
864 : /* }}} */
865 :
866 : /* {{{ proto int hexdec(string hexadecimal_number)
867 : Returns the decimal equivalent of the hexadecimal number */
868 : PHP_FUNCTION(hexdec)
869 54 : {
870 : zval **arg;
871 :
872 54 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
873 2 : WRONG_PARAM_COUNT;
874 : }
875 :
876 52 : convert_to_string_ex(arg);
877 :
878 51 : if(_php_math_basetozval(*arg, 16, return_value) != SUCCESS) {
879 0 : RETURN_FALSE;
880 : }
881 : }
882 : /* }}} */
883 :
884 : /* {{{ proto int octdec(string octal_number)
885 : Returns the decimal equivalent of an octal string */
886 : PHP_FUNCTION(octdec)
887 46 : {
888 : zval **arg;
889 :
890 46 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
891 2 : WRONG_PARAM_COUNT;
892 : }
893 :
894 44 : convert_to_string_ex(arg);
895 :
896 43 : if(_php_math_basetozval(*arg, 8, return_value) != SUCCESS) {
897 0 : RETURN_FALSE;
898 : }
899 : }
900 : /* }}} */
901 :
902 : /* {{{ proto string decbin(int decimal_number)
903 : Returns a string containing a binary representation of the number */
904 : PHP_FUNCTION(decbin)
905 1072 : {
906 : zval **arg;
907 : char *result;
908 :
909 1072 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
910 2 : WRONG_PARAM_COUNT;
911 : }
912 :
913 1070 : convert_to_long_ex(arg);
914 :
915 1070 : result = _php_math_longtobase(*arg, 2);
916 1070 : RETURN_STRING(result, 0);
917 : }
918 : /* }}} */
919 :
920 : /* {{{ proto string decoct(int decimal_number)
921 : Returns a string containing an octal representation of the given number */
922 : PHP_FUNCTION(decoct)
923 44 : {
924 : zval **arg;
925 : char *result;
926 :
927 44 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
928 2 : WRONG_PARAM_COUNT;
929 : }
930 :
931 42 : convert_to_long_ex(arg);
932 :
933 42 : result = _php_math_longtobase(*arg, 8);
934 42 : RETURN_STRING(result, 0);
935 : }
936 : /* }}} */
937 :
938 : /* {{{ proto string dechex(int decimal_number)
939 : Returns a string containing a hexadecimal representation of the given number */
940 : PHP_FUNCTION(dechex)
941 43 : {
942 : zval **arg;
943 : char *result;
944 :
945 43 : if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
946 2 : WRONG_PARAM_COUNT;
947 : }
948 :
949 41 : convert_to_long_ex(arg);
950 :
951 41 : result = _php_math_longtobase(*arg, 16);
952 41 : RETURN_STRING(result, 0);
953 : }
954 : /* }}} */
955 :
956 : /* {{{ proto string base_convert(string number, int frombase, int tobase)
957 : Converts a number in a string from any base <= 36 to any base <= 36 */
958 : PHP_FUNCTION(base_convert)
959 329 : {
960 : zval **number, **frombase, **tobase, temp;
961 : char *result;
962 :
963 329 : if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &number, &frombase, &tobase) == FAILURE) {
964 3 : WRONG_PARAM_COUNT;
965 : }
966 326 : convert_to_string_ex(number);
967 325 : convert_to_long_ex(frombase);
968 325 : convert_to_long_ex(tobase);
969 325 : if (Z_LVAL_PP(frombase) < 2 || Z_LVAL_PP(frombase) > 36) {
970 24 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", Z_LVAL_PP(frombase));
971 24 : RETURN_FALSE;
972 : }
973 301 : if (Z_LVAL_PP(tobase) < 2 || Z_LVAL_PP(tobase) > 36) {
974 24 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", Z_LVAL_PP(tobase));
975 24 : RETURN_FALSE;
976 : }
977 :
978 277 : if(_php_math_basetozval(*number, Z_LVAL_PP(frombase), &temp) != SUCCESS) {
979 0 : RETURN_FALSE;
980 : }
981 277 : result = _php_math_zvaltobase(&temp, Z_LVAL_PP(tobase) TSRMLS_CC);
982 277 : RETVAL_STRING(result, 0);
983 : }
984 : /* }}} */
985 :
986 : /* {{{ _php_math_number_format
987 : */
988 : PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
989 68 : {
990 68 : char *tmpbuf = NULL, *resbuf;
991 : char *s, *t; /* source, target */
992 : char *dp;
993 : int integral;
994 68 : int tmplen, reslen=0;
995 68 : int count=0;
996 68 : int is_negative=0;
997 :
998 68 : if (d < 0) {
999 9 : is_negative = 1;
1000 9 : d = -d;
1001 : }
1002 :
1003 68 : dec = MAX(0, dec);
1004 68 : d = php_round(d, dec);
1005 :
1006 68 : tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d);
1007 :
1008 68 : if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
1009 2 : return tmpbuf;
1010 : }
1011 :
1012 : /* find decimal point, if expected */
1013 66 : if (dec) {
1014 51 : dp = strpbrk(tmpbuf, ".,");
1015 : } else {
1016 15 : dp = NULL;
1017 : }
1018 :
1019 : /* calculate the length of the return buffer */
1020 66 : if (dp) {
1021 51 : integral = dp - tmpbuf;
1022 : } else {
1023 : /* no decimal point was found */
1024 15 : integral = tmplen;
1025 : }
1026 :
1027 : /* allow for thousand separators */
1028 66 : if (thousand_sep) {
1029 64 : integral += (integral-1) / 3;
1030 : }
1031 :
1032 66 : reslen = integral;
1033 :
1034 66 : if (dec) {
1035 51 : reslen += dec;
1036 :
1037 51 : if (dec_point) {
1038 48 : reslen++;
1039 : }
1040 : }
1041 :
1042 : /* add a byte for minus sign */
1043 66 : if (is_negative) {
1044 9 : reslen++;
1045 : }
1046 66 : resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */
1047 :
1048 66 : s = tmpbuf+tmplen-1;
1049 66 : t = resbuf+reslen;
1050 66 : *t-- = '\0';
1051 :
1052 : /* copy the decimal places.
1053 : * Take care, as the sprintf implementation may return less places than
1054 : * we requested due to internal buffer limitations */
1055 66 : if (dec) {
1056 51 : int declen = dp ? s - dp : 0;
1057 51 : int topad = dec > declen ? dec - declen : 0;
1058 :
1059 : /* pad with '0's */
1060 2552 : while (topad--) {
1061 2450 : *t-- = '0';
1062 : }
1063 :
1064 51 : if (dp) {
1065 51 : s -= declen + 1; /* +1 to skip the point */
1066 51 : t -= declen;
1067 :
1068 : /* now copy the chars after the point */
1069 51 : memcpy(t + 1, dp + 1, declen);
1070 : }
1071 :
1072 : /* add decimal point */
1073 51 : if (dec_point) {
1074 48 : *t-- = dec_point;
1075 : }
1076 : }
1077 :
1078 : /* copy the numbers before the decimal point, adding thousand
1079 : * separator every three digits */
1080 791 : while(s >= tmpbuf) {
1081 659 : *t-- = *s--;
1082 659 : if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
1083 180 : *t-- = thousand_sep;
1084 : }
1085 : }
1086 :
1087 : /* and a minus sign, if needed */
1088 66 : if (is_negative) {
1089 9 : *t-- = '-';
1090 : }
1091 :
1092 66 : efree(tmpbuf);
1093 :
1094 66 : return resbuf;
1095 : }
1096 : /* }}} */
1097 :
1098 : /* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_seperator, string thousands_seperator]])
1099 : Formats a number with grouped thousands */
1100 : PHP_FUNCTION(number_format)
1101 74 : {
1102 : zval **num, **dec, **t_s, **d_p;
1103 74 : char thousand_sep=',', dec_point='.';
1104 :
1105 74 : switch(ZEND_NUM_ARGS()) {
1106 : case 1:
1107 12 : if (zend_get_parameters_ex(1, &num)==FAILURE) {
1108 0 : RETURN_FALSE;
1109 : }
1110 12 : convert_to_double_ex(num);
1111 12 : RETURN_STRING(_php_math_number_format(Z_DVAL_PP(num), 0, dec_point, thousand_sep), 0);
1112 : break;
1113 : case 2:
1114 23 : if (zend_get_parameters_ex(2, &num, &dec)==FAILURE) {
1115 0 : RETURN_FALSE;
1116 : }
1117 23 : convert_to_double_ex(num);
1118 23 : convert_to_long_ex(dec);
1119 23 : RETURN_STRING(_php_math_number_format(Z_DVAL_PP(num), Z_LVAL_PP(dec), dec_point, thousand_sep), 0);
1120 : break;
1121 : case 4:
1122 33 : if (zend_get_parameters_ex(4, &num, &dec, &d_p, &t_s)==FAILURE) {
1123 0 : RETURN_FALSE;
1124 : }
1125 33 : convert_to_double_ex(num);
1126 33 : convert_to_long_ex(dec);
1127 :
1128 33 : if (Z_TYPE_PP(d_p) != IS_NULL) {
1129 32 : convert_to_string_ex(d_p);
1130 32 : if (Z_STRLEN_PP(d_p)>=1) {
1131 25 : dec_point=Z_STRVAL_PP(d_p)[0];
1132 7 : } else if (Z_STRLEN_PP(d_p)==0) {
1133 7 : dec_point=0;
1134 : }
1135 : }
1136 33 : if (Z_TYPE_PP(t_s) != IS_NULL) {
1137 33 : convert_to_string_ex(t_s);
1138 33 : if (Z_STRLEN_PP(t_s)>=1) {
1139 31 : thousand_sep=Z_STRVAL_PP(t_s)[0];
1140 2 : } else if(Z_STRLEN_PP(t_s)==0) {
1141 2 : thousand_sep=0;
1142 : }
1143 : }
1144 33 : RETURN_STRING(_php_math_number_format(Z_DVAL_PP(num), Z_LVAL_PP(dec), dec_point, thousand_sep), 0);
1145 : break;
1146 : default:
1147 6 : WRONG_PARAM_COUNT;
1148 : break;
1149 : }
1150 : }
1151 : /* }}} */
1152 :
1153 : /* {{{ proto float fmod(float x, float y)
1154 : Returns the remainder of dividing x by y as a float */
1155 : PHP_FUNCTION(fmod)
1156 199 : {
1157 : double num1, num2;
1158 :
1159 199 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
1160 19 : return;
1161 : }
1162 :
1163 180 : Z_DVAL_P(return_value) = fmod(num1, num2);
1164 180 : Z_TYPE_P(return_value) = IS_DOUBLE;
1165 : }
1166 : /* }}} */
1167 :
1168 :
1169 :
1170 : /*
1171 : * Local variables:
1172 : * tab-width: 4
1173 : * c-basic-offset: 4
1174 : * End:
1175 : * vim600: fdm=marker
1176 : * vim: noet sw=4 ts=4
1177 : */
|