Annotation of embedaddon/php/ext/standard/math.c, revision 1.1.1.1
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2012 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 321634 2012-01-01 13:15:04Z felipe $ */
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 > 1e23) {
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 = 16;
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: /* make sure we're dealing with numbers */
608: convert_scalar_to_number(zbase TSRMLS_CC);
609: convert_scalar_to_number(zexp TSRMLS_CC);
610:
611: /* if both base and exponent were longs, we'll try to get a long out */
612: if (Z_TYPE_P(zbase) == IS_LONG && Z_TYPE_P(zexp) == IS_LONG && Z_LVAL_P(zexp) >= 0) {
613: long l1 = 1, l2 = Z_LVAL_P(zbase), i = Z_LVAL_P(zexp);
614:
615: if (i == 0) {
616: RETURN_LONG(1L);
617: } else if (l2 == 0) {
618: RETURN_LONG(0);
619: }
620:
621: /* calculate pow(long,long) in O(log exp) operations, bail if overflow */
622: while (i >= 1) {
623: int overflow;
624: double dval = 0.0;
625:
626: if (i % 2) {
627: --i;
628: ZEND_SIGNED_MULTIPLY_LONG(l1,l2,l1,dval,overflow);
629: if (overflow) RETURN_DOUBLE(dval * pow(l2,i));
630: } else {
631: i /= 2;
632: ZEND_SIGNED_MULTIPLY_LONG(l2,l2,l2,dval,overflow);
633: if (overflow) RETURN_DOUBLE((double)l1 * pow(dval,i));
634: }
635: if (i == 0) {
636: RETURN_LONG(l1);
637: }
638: }
639: }
640: convert_to_double(zbase);
641: convert_to_double(zexp);
642:
643: RETURN_DOUBLE(pow(Z_DVAL_P(zbase), Z_DVAL_P(zexp)));
644: }
645: /* }}} */
646:
647: /* {{{ proto float exp(float number)
648: Returns e raised to the power of the number */
649: PHP_FUNCTION(exp)
650: {
651: double num;
652:
653: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
654: return;
655: }
656:
657: RETURN_DOUBLE(exp(num));
658: }
659: /* }}} */
660:
661: /* {{{ proto float expm1(float number)
662: Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
663: /*
664: WARNING: this function is expermental: it could change its name or
665: disappear in the next version of PHP!
666: */
667: PHP_FUNCTION(expm1)
668: {
669: double num;
670:
671: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
672: return;
673: }
674: RETURN_DOUBLE(php_expm1(num));
675: }
676: /* }}} */
677:
678: /* {{{ proto float log1p(float number)
679: Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
680: /*
681: WARNING: this function is expermental: it could change its name or
682: disappear in the next version of PHP!
683: */
684: PHP_FUNCTION(log1p)
685: {
686: double num;
687:
688: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
689: return;
690: }
691: RETURN_DOUBLE(php_log1p(num));
692: }
693: /* }}} */
694:
695: /* {{{ proto float log(float number, [float base])
696: Returns the natural logarithm of the number, or the base log if base is specified */
697: PHP_FUNCTION(log)
698: {
699: double num, base = 0;
700:
701: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) == FAILURE) {
702: return;
703: }
704: if (ZEND_NUM_ARGS() == 1) {
705: RETURN_DOUBLE(log(num));
706: }
707: if (base <= 0.0) {
708: php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");
709: RETURN_FALSE;
710: }
711: if (base == 1) {
712: RETURN_DOUBLE(php_get_nan());
713: } else {
714: RETURN_DOUBLE(log(num) / log(base));
715: }
716: }
717: /* }}} */
718:
719: /* {{{ proto float log10(float number)
720: Returns the base-10 logarithm of the number */
721: PHP_FUNCTION(log10)
722: {
723: double num;
724:
725: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
726: return;
727: }
728: RETURN_DOUBLE(log10(num));
729: }
730: /* }}} */
731:
732: /* {{{ proto float sqrt(float number)
733: Returns the square root of the number */
734: PHP_FUNCTION(sqrt)
735: {
736: double num;
737:
738: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
739: return;
740: }
741: RETURN_DOUBLE(sqrt(num));
742: }
743: /* }}} */
744:
745: /* {{{ proto float hypot(float num1, float num2)
746: Returns sqrt(num1*num1 + num2*num2) */
747: PHP_FUNCTION(hypot)
748: {
749: double num1, num2;
750:
751: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
752: return;
753: }
754: #if HAVE_HYPOT
755: RETURN_DOUBLE(hypot(num1, num2));
756: #elif defined(_MSC_VER)
757: RETURN_DOUBLE(_hypot(num1, num2));
758: #else
759: RETURN_DOUBLE(sqrt((num1 * num1) + (num2 * num2)));
760: #endif
761: }
762: /* }}} */
763:
764: /* {{{ proto float deg2rad(float number)
765: Converts the number in degrees to the radian equivalent */
766: PHP_FUNCTION(deg2rad)
767: {
768: double deg;
769:
770: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", °) == FAILURE) {
771: return;
772: }
773: RETURN_DOUBLE((deg / 180.0) * M_PI);
774: }
775: /* }}} */
776:
777: /* {{{ proto float rad2deg(float number)
778: Converts the radian number to the equivalent number in degrees */
779: PHP_FUNCTION(rad2deg)
780: {
781: double rad;
782:
783: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &rad) == FAILURE) {
784: return;
785: }
786: RETURN_DOUBLE((rad / M_PI) * 180);
787: }
788: /* }}} */
789:
790: /* {{{ _php_math_basetolong */
791: /*
792: * Convert a string representation of a base(2-36) number to a long.
793: */
794: PHPAPI long _php_math_basetolong(zval *arg, int base)
795: {
796: long num = 0, digit, onum;
797: int i;
798: char c, *s;
799:
800: if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
801: return 0;
802: }
803:
804: s = Z_STRVAL_P(arg);
805:
806: for (i = Z_STRLEN_P(arg); i > 0; i--) {
807: c = *s++;
808:
809: digit = (c >= '0' && c <= '9') ? c - '0'
810: : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
811: : (c >= 'a' && c <= 'z') ? c - 'a' + 10
812: : base;
813:
814: if (digit >= base) {
815: continue;
816: }
817:
818: onum = num;
819: num = num * base + digit;
820: if (num > onum)
821: continue;
822:
823: {
824: TSRMLS_FETCH();
825:
826: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
827: return LONG_MAX;
828: }
829: }
830:
831: return num;
832: }
833: /* }}} */
834:
835: /* {{{ _php_math_basetozval */
836: /*
837: * Convert a string representation of a base(2-36) number to a zval.
838: */
839: PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
840: {
841: long num = 0;
842: double fnum = 0;
843: int i;
844: int mode = 0;
845: char c, *s;
846: long cutoff;
847: int cutlim;
848:
849: if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
850: return FAILURE;
851: }
852:
853: s = Z_STRVAL_P(arg);
854:
855: cutoff = LONG_MAX / base;
856: cutlim = LONG_MAX % base;
857:
858: for (i = Z_STRLEN_P(arg); i > 0; i--) {
859: c = *s++;
860:
861: /* might not work for EBCDIC */
862: if (c >= '0' && c <= '9')
863: c -= '0';
864: else if (c >= 'A' && c <= 'Z')
865: c -= 'A' - 10;
866: else if (c >= 'a' && c <= 'z')
867: c -= 'a' - 10;
868: else
869: continue;
870:
871: if (c >= base)
872: continue;
873:
874: switch (mode) {
875: case 0: /* Integer */
876: if (num < cutoff || (num == cutoff && c <= cutlim)) {
877: num = num * base + c;
878: break;
879: } else {
880: fnum = num;
881: mode = 1;
882: }
883: /* fall-through */
884: case 1: /* Float */
885: fnum = fnum * base + c;
886: }
887: }
888:
889: if (mode == 1) {
890: ZVAL_DOUBLE(ret, fnum);
891: } else {
892: ZVAL_LONG(ret, num);
893: }
894: return SUCCESS;
895: }
896: /* }}} */
897:
898: /* {{{ _php_math_longtobase */
899: /*
900: * Convert a long to a string containing a base(2-36) representation of
901: * the number.
902: */
903: PHPAPI char * _php_math_longtobase(zval *arg, int base)
904: {
905: static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
906: char buf[(sizeof(unsigned long) << 3) + 1];
907: char *ptr, *end;
908: unsigned long value;
909:
910: if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
911: return STR_EMPTY_ALLOC();
912: }
913:
914: value = Z_LVAL_P(arg);
915:
916: end = ptr = buf + sizeof(buf) - 1;
917: *ptr = '\0';
918:
919: do {
920: *--ptr = digits[value % base];
921: value /= base;
922: } while (ptr > buf && value);
923:
924: return estrndup(ptr, end - ptr);
925: }
926: /* }}} */
927:
928: /* {{{ _php_math_zvaltobase */
929: /*
930: * Convert a zval to a string containing a base(2-36) representation of
931: * the number.
932: */
933: PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
934: {
935: static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
936:
937: if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
938: return STR_EMPTY_ALLOC();
939: }
940:
941: if (Z_TYPE_P(arg) == IS_DOUBLE) {
942: double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
943: char *ptr, *end;
944: char buf[(sizeof(double) << 3) + 1];
945:
946: /* Don't try to convert +/- infinity */
947: if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
948: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
949: return STR_EMPTY_ALLOC();
950: }
951:
952: end = ptr = buf + sizeof(buf) - 1;
953: *ptr = '\0';
954:
955: do {
956: *--ptr = digits[(int) fmod(fvalue, base)];
957: fvalue /= base;
958: } while (ptr > buf && fabs(fvalue) >= 1);
959:
960: return estrndup(ptr, end - ptr);
961: }
962:
963: return _php_math_longtobase(arg, base);
964: }
965: /* }}} */
966:
967: /* {{{ proto int bindec(string binary_number)
968: Returns the decimal equivalent of the binary number */
969: PHP_FUNCTION(bindec)
970: {
971: zval **arg;
972:
973: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
974: return;
975: }
976: convert_to_string_ex(arg);
977: if (_php_math_basetozval(*arg, 2, return_value) == FAILURE) {
978: RETURN_FALSE;
979: }
980: }
981: /* }}} */
982:
983: /* {{{ proto int hexdec(string hexadecimal_number)
984: Returns the decimal equivalent of the hexadecimal number */
985: PHP_FUNCTION(hexdec)
986: {
987: zval **arg;
988:
989: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
990: return;
991: }
992: convert_to_string_ex(arg);
993: if (_php_math_basetozval(*arg, 16, return_value) == FAILURE) {
994: RETURN_FALSE;
995: }
996: }
997: /* }}} */
998:
999: /* {{{ proto int octdec(string octal_number)
1000: Returns the decimal equivalent of an octal string */
1001: PHP_FUNCTION(octdec)
1002: {
1003: zval **arg;
1004:
1005: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1006: return;
1007: }
1008: convert_to_string_ex(arg);
1009: if (_php_math_basetozval(*arg, 8, return_value) == FAILURE) {
1010: RETURN_FALSE;
1011: }
1012: }
1013: /* }}} */
1014:
1015: /* {{{ proto string decbin(int decimal_number)
1016: Returns a string containing a binary representation of the number */
1017: PHP_FUNCTION(decbin)
1018: {
1019: zval **arg;
1020: char *result;
1021:
1022: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1023: return;
1024: }
1025: convert_to_long_ex(arg);
1026: result = _php_math_longtobase(*arg, 2);
1027: RETURN_STRING(result, 0);
1028: }
1029: /* }}} */
1030:
1031: /* {{{ proto string decoct(int decimal_number)
1032: Returns a string containing an octal representation of the given number */
1033: PHP_FUNCTION(decoct)
1034: {
1035: zval **arg;
1036: char *result;
1037:
1038: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1039: return;
1040: }
1041: convert_to_long_ex(arg);
1042: result = _php_math_longtobase(*arg, 8);
1043: RETURN_STRING(result, 0);
1044: }
1045: /* }}} */
1046:
1047: /* {{{ proto string dechex(int decimal_number)
1048: Returns a string containing a hexadecimal representation of the given number */
1049: PHP_FUNCTION(dechex)
1050: {
1051: zval **arg;
1052: char *result;
1053:
1054: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1055: return;
1056: }
1057: convert_to_long_ex(arg);
1058: result = _php_math_longtobase(*arg, 16);
1059: RETURN_STRING(result, 0);
1060: }
1061: /* }}} */
1062:
1063: /* {{{ proto string base_convert(string number, int frombase, int tobase)
1064: Converts a number in a string from any base <= 36 to any base <= 36 */
1065: PHP_FUNCTION(base_convert)
1066: {
1067: zval **number, temp;
1068: long frombase, tobase;
1069: char *result;
1070:
1071: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zll", &number, &frombase, &tobase) == FAILURE) {
1072: return;
1073: }
1074: convert_to_string_ex(number);
1075:
1076: if (frombase < 2 || frombase > 36) {
1077: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", frombase);
1078: RETURN_FALSE;
1079: }
1080: if (tobase < 2 || tobase > 36) {
1081: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", tobase);
1082: RETURN_FALSE;
1083: }
1084:
1085: if(_php_math_basetozval(*number, frombase, &temp) == FAILURE) {
1086: RETURN_FALSE;
1087: }
1088: result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
1089: RETVAL_STRING(result, 0);
1090: }
1091: /* }}} */
1092:
1093: /* {{{ _php_math_number_format
1094: */
1095: PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1096: {
1097: char *tmpbuf = NULL, *resbuf;
1098: char *s, *t; /* source, target */
1099: char *dp;
1100: int integral;
1101: int tmplen, reslen=0;
1102: int count=0;
1103: int is_negative=0;
1104:
1105: if (d < 0) {
1106: is_negative = 1;
1107: d = -d;
1108: }
1109:
1110: dec = MAX(0, dec);
1111: d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1112:
1113: tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d);
1114:
1115: if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
1116: return tmpbuf;
1117: }
1118:
1119: /* find decimal point, if expected */
1120: if (dec) {
1121: dp = strpbrk(tmpbuf, ".,");
1122: } else {
1123: dp = NULL;
1124: }
1125:
1126: /* calculate the length of the return buffer */
1127: if (dp) {
1128: integral = dp - tmpbuf;
1129: } else {
1130: /* no decimal point was found */
1131: integral = tmplen;
1132: }
1133:
1134: /* allow for thousand separators */
1135: if (thousand_sep) {
1136: integral += (integral-1) / 3;
1137: }
1138:
1139: reslen = integral;
1140:
1141: if (dec) {
1142: reslen += dec;
1143:
1144: if (dec_point) {
1145: reslen++;
1146: }
1147: }
1148:
1149: /* add a byte for minus sign */
1150: if (is_negative) {
1151: reslen++;
1152: }
1153: resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */
1154:
1155: s = tmpbuf+tmplen-1;
1156: t = resbuf+reslen;
1157: *t-- = '\0';
1158:
1159: /* copy the decimal places.
1160: * Take care, as the sprintf implementation may return less places than
1161: * we requested due to internal buffer limitations */
1162: if (dec) {
1163: int declen = dp ? s - dp : 0;
1164: int topad = dec > declen ? dec - declen : 0;
1165:
1166: /* pad with '0's */
1167: while (topad--) {
1168: *t-- = '0';
1169: }
1170:
1171: if (dp) {
1172: s -= declen + 1; /* +1 to skip the point */
1173: t -= declen;
1174:
1175: /* now copy the chars after the point */
1176: memcpy(t + 1, dp + 1, declen);
1177: }
1178:
1179: /* add decimal point */
1180: if (dec_point) {
1181: *t-- = dec_point;
1182: }
1183: }
1184:
1185: /* copy the numbers before the decimal point, adding thousand
1186: * separator every three digits */
1187: while(s >= tmpbuf) {
1188: *t-- = *s--;
1189: if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
1190: *t-- = thousand_sep;
1191: }
1192: }
1193:
1194: /* and a minus sign, if needed */
1195: if (is_negative) {
1196: *t-- = '-';
1197: }
1198:
1199: efree(tmpbuf);
1200:
1201: return resbuf;
1202: }
1203: /* }}} */
1204:
1205: /* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_seperator, string thousands_seperator]])
1206: Formats a number with grouped thousands */
1207: PHP_FUNCTION(number_format)
1208: {
1209: double num;
1210: long dec = 0;
1211: char *thousand_sep = NULL, *dec_point = NULL;
1212: char thousand_sep_chr = ',', dec_point_chr = '.';
1213: int thousand_sep_len = 0, dec_point_len = 0;
1214:
1215: 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) {
1216: return;
1217: }
1218:
1219: switch(ZEND_NUM_ARGS()) {
1220: case 1:
1221: RETURN_STRING(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr), 0);
1222: break;
1223: case 2:
1224: RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1225: break;
1226: case 4:
1227: if (dec_point != NULL) {
1228: if (dec_point_len) {
1229: dec_point_chr = dec_point[0];
1230: } else {
1231: dec_point_chr = 0;
1232: }
1233: }
1234: if (thousand_sep != NULL) {
1235: if (thousand_sep_len) {
1236: thousand_sep_chr = thousand_sep[0];
1237: } else {
1238: thousand_sep_chr = 0;
1239: }
1240: }
1241: RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1242: break;
1243: default:
1244: WRONG_PARAM_COUNT;
1245: break;
1246: }
1247: }
1248: /* }}} */
1249:
1250: /* {{{ proto float fmod(float x, float y)
1251: Returns the remainder of dividing x by y as a float */
1252: PHP_FUNCTION(fmod)
1253: {
1254: double num1, num2;
1255:
1256: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
1257: return;
1258: }
1259: RETURN_DOUBLE(fmod(num1, num2));
1260: }
1261: /* }}} */
1262:
1263:
1264:
1265: /*
1266: * Local variables:
1267: * tab-width: 4
1268: * c-basic-offset: 4
1269: * End:
1270: * vim600: fdm=marker
1271: * vim: noet sw=4 ts=4
1272: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>