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