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