1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Andi Gutmans <andi@zend.com> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: bcmath.c 276986 2009-03-10 23:40:06Z helly $ */
20 :
21 : #ifdef HAVE_CONFIG_H
22 : #include "config.h"
23 : #endif
24 :
25 : #include "php.h"
26 :
27 : #if HAVE_BCMATH
28 :
29 : #include "php_ini.h"
30 : #include "ext/standard/info.h"
31 : #include "php_bcmath.h"
32 : #include "libbcmath/src/bcmath.h"
33 :
34 : ZEND_DECLARE_MODULE_GLOBALS(bcmath)
35 : static PHP_GINIT_FUNCTION(bcmath);
36 : static PHP_GSHUTDOWN_FUNCTION(bcmath);
37 :
38 : /* {{{ arginfo */
39 : ZEND_BEGIN_ARG_INFO_EX(arginfo_bcadd, 0, 0, 2)
40 : ZEND_ARG_INFO(0, left_operand)
41 : ZEND_ARG_INFO(0, right_operand)
42 : ZEND_ARG_INFO(0, scale)
43 : ZEND_END_ARG_INFO()
44 :
45 : ZEND_BEGIN_ARG_INFO_EX(arginfo_bcsub, 0, 0, 2)
46 : ZEND_ARG_INFO(0, left_operand)
47 : ZEND_ARG_INFO(0, right_operand)
48 : ZEND_ARG_INFO(0, scale)
49 : ZEND_END_ARG_INFO()
50 :
51 : ZEND_BEGIN_ARG_INFO_EX(arginfo_bcmul, 0, 0, 2)
52 : ZEND_ARG_INFO(0, left_operand)
53 : ZEND_ARG_INFO(0, right_operand)
54 : ZEND_ARG_INFO(0, scale)
55 : ZEND_END_ARG_INFO()
56 :
57 : ZEND_BEGIN_ARG_INFO_EX(arginfo_bcdiv, 0, 0, 2)
58 : ZEND_ARG_INFO(0, left_operand)
59 : ZEND_ARG_INFO(0, right_operand)
60 : ZEND_ARG_INFO(0, scale)
61 : ZEND_END_ARG_INFO()
62 :
63 : ZEND_BEGIN_ARG_INFO(arginfo_bcmod, 0)
64 : ZEND_ARG_INFO(0, left_operand)
65 : ZEND_ARG_INFO(0, right_operand)
66 : ZEND_END_ARG_INFO()
67 :
68 : ZEND_BEGIN_ARG_INFO_EX(arginfo_bcpowmod, 0, 0, 3)
69 : ZEND_ARG_INFO(0, x)
70 : ZEND_ARG_INFO(0, y)
71 : ZEND_ARG_INFO(0, mod)
72 : ZEND_ARG_INFO(0, scale)
73 : ZEND_END_ARG_INFO()
74 :
75 : ZEND_BEGIN_ARG_INFO_EX(arginfo_bcpow, 0, 0, 2)
76 : ZEND_ARG_INFO(0, x)
77 : ZEND_ARG_INFO(0, y)
78 : ZEND_ARG_INFO(0, scale)
79 : ZEND_END_ARG_INFO()
80 :
81 : ZEND_BEGIN_ARG_INFO_EX(arginfo_bcsqrt, 0, 0, 1)
82 : ZEND_ARG_INFO(0, operand)
83 : ZEND_ARG_INFO(0, scale)
84 : ZEND_END_ARG_INFO()
85 :
86 : ZEND_BEGIN_ARG_INFO_EX(arginfo_bccomp, 0, 0, 2)
87 : ZEND_ARG_INFO(0, left_operand)
88 : ZEND_ARG_INFO(0, right_operand)
89 : ZEND_ARG_INFO(0, scale)
90 : ZEND_END_ARG_INFO()
91 :
92 : ZEND_BEGIN_ARG_INFO(arginfo_bcscale, 0)
93 : ZEND_ARG_INFO(0, scale)
94 : ZEND_END_ARG_INFO()
95 :
96 : /* }}} */
97 :
98 : const zend_function_entry bcmath_functions[] = {
99 : PHP_FE(bcadd, arginfo_bcadd)
100 : PHP_FE(bcsub, arginfo_bcsub)
101 : PHP_FE(bcmul, arginfo_bcmul)
102 : PHP_FE(bcdiv, arginfo_bcdiv)
103 : PHP_FE(bcmod, arginfo_bcmod)
104 : PHP_FE(bcpow, arginfo_bcpow)
105 : PHP_FE(bcsqrt, arginfo_bcsqrt)
106 : PHP_FE(bcscale, arginfo_bcscale)
107 : PHP_FE(bccomp, arginfo_bccomp)
108 : PHP_FE(bcpowmod, arginfo_bcpowmod)
109 : {NULL, NULL, NULL}
110 : };
111 :
112 : zend_module_entry bcmath_module_entry = {
113 : STANDARD_MODULE_HEADER,
114 : "bcmath",
115 : bcmath_functions,
116 : PHP_MINIT(bcmath),
117 : PHP_MSHUTDOWN(bcmath),
118 : NULL,
119 : NULL,
120 : PHP_MINFO(bcmath),
121 : NO_VERSION_YET,
122 : PHP_MODULE_GLOBALS(bcmath),
123 : PHP_GINIT(bcmath),
124 : PHP_GSHUTDOWN(bcmath),
125 : NULL,
126 : STANDARD_MODULE_PROPERTIES_EX
127 : };
128 :
129 : #ifdef COMPILE_DL_BCMATH
130 : ZEND_GET_MODULE(bcmath)
131 : #endif
132 :
133 : /* {{{ PHP_INI */
134 : PHP_INI_BEGIN()
135 : STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateLongGEZero, bc_precision, zend_bcmath_globals, bcmath_globals)
136 : PHP_INI_END()
137 : /* }}} */
138 :
139 : /* {{{ PHP_GINIT_FUNCTION
140 : */
141 : static PHP_GINIT_FUNCTION(bcmath)
142 17007 : {
143 17007 : bcmath_globals->bc_precision = 0;
144 17007 : bc_init_numbers(TSRMLS_C);
145 17007 : }
146 : /* }}} */
147 :
148 : /* {{{ PHP_GSHUTDOWN_FUNCTION
149 : */
150 : static PHP_GSHUTDOWN_FUNCTION(bcmath)
151 17039 : {
152 17039 : _bc_free_num_ex(&bcmath_globals->_zero_, 1);
153 17039 : _bc_free_num_ex(&bcmath_globals->_one_, 1);
154 17039 : _bc_free_num_ex(&bcmath_globals->_two_, 1);
155 17039 : }
156 : /* }}} */
157 :
158 : /* {{{ PHP_MINIT_FUNCTION
159 : */
160 : PHP_MINIT_FUNCTION(bcmath)
161 17007 : {
162 17007 : REGISTER_INI_ENTRIES();
163 :
164 17007 : return SUCCESS;
165 : }
166 : /* }}} */
167 :
168 : /* {{{ PHP_MSHUTDOWN_FUNCTION
169 : */
170 : PHP_MSHUTDOWN_FUNCTION(bcmath)
171 17039 : {
172 17039 : UNREGISTER_INI_ENTRIES();
173 :
174 17039 : return SUCCESS;
175 : }
176 : /* }}} */
177 :
178 : /* {{{ PHP_MINFO_FUNCTION
179 : */
180 : PHP_MINFO_FUNCTION(bcmath)
181 43 : {
182 43 : php_info_print_table_start();
183 43 : php_info_print_table_row(2, "BCMath support", "enabled");
184 43 : php_info_print_table_end();
185 43 : DISPLAY_INI_ENTRIES();
186 43 : }
187 : /* }}} */
188 :
189 : /* {{{ php_str2num
190 : Convert to bc_num detecting scale */
191 : static void php_str2num(bc_num *num, char *str TSRMLS_DC)
192 53 : {
193 : char *p;
194 :
195 53 : if (!(p = strchr(str, '.'))) {
196 50 : bc_str2num(num, str, 0 TSRMLS_CC);
197 50 : return;
198 : }
199 :
200 3 : bc_str2num(num, str, strlen(p+1) TSRMLS_CC);
201 : }
202 : /* }}} */
203 :
204 : /* {{{ proto string bcadd(string left_operand, string right_operand [, int scale]) U
205 : Returns the sum of two arbitrary precision numbers */
206 : PHP_FUNCTION(bcadd)
207 7 : {
208 : char *left, *right;
209 : int left_len, right_len;
210 7 : long scale = BCG(bc_precision);
211 : bc_num first, second, result;
212 :
213 7 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale) == FAILURE) {
214 0 : return;
215 : }
216 :
217 7 : if ((int)scale < 0) {
218 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
219 0 : scale = 0;
220 : }
221 :
222 7 : bc_init_num(&first TSRMLS_CC);
223 7 : bc_init_num(&second TSRMLS_CC);
224 7 : bc_init_num(&result TSRMLS_CC);
225 7 : php_str2num(&first, left TSRMLS_CC);
226 7 : php_str2num(&second, right TSRMLS_CC);
227 :
228 7 : bc_add(first, second, &result, scale);
229 7 : if (result->n_scale > scale) {
230 0 : result->n_scale = scale;
231 : }
232 :
233 7 : RETVAL_STRING(bc_num2str(result), 0);
234 :
235 7 : bc_free_num(&first);
236 7 : bc_free_num(&second);
237 7 : bc_free_num(&result);
238 : }
239 : /* }}} */
240 :
241 : /* {{{ proto string bcsub(string left_operand, string right_operand [, int scale]) U
242 : Returns the difference between two arbitrary precision numbers */
243 : PHP_FUNCTION(bcsub)
244 3 : {
245 : char *left, *right;
246 : int left_len, right_len;
247 3 : long scale = BCG(bc_precision);
248 : bc_num first, second, result;
249 :
250 3 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale) == FAILURE) {
251 0 : return;
252 : }
253 :
254 3 : if ((int)scale < 0) {
255 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
256 0 : scale = 0;
257 : }
258 :
259 3 : bc_init_num(&first TSRMLS_CC);
260 3 : bc_init_num(&second TSRMLS_CC);
261 3 : bc_init_num(&result TSRMLS_CC);
262 3 : php_str2num(&first, left TSRMLS_CC);
263 3 : php_str2num(&second, right TSRMLS_CC);
264 :
265 3 : bc_sub(first, second, &result, scale);
266 3 : if (result->n_scale > scale) {
267 0 : result->n_scale = scale;
268 : }
269 :
270 3 : RETVAL_STRING(bc_num2str(result), 0);
271 :
272 3 : bc_free_num(&first);
273 3 : bc_free_num(&second);
274 3 : bc_free_num(&result);
275 : }
276 : /* }}} */
277 :
278 : /* {{{ proto string bcmul(string left_operand, string right_operand [, int scale]) U
279 : Returns the multiplication of two arbitrary precision numbers */
280 : PHP_FUNCTION(bcmul)
281 4 : {
282 : char *left, *right;
283 : int left_len, right_len;
284 4 : long scale = BCG(bc_precision);
285 : bc_num first, second, result;
286 :
287 4 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale) == FAILURE) {
288 0 : return;
289 : }
290 :
291 4 : if ((int)scale < 0) {
292 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
293 0 : scale = 0;
294 : }
295 :
296 4 : bc_init_num(&first TSRMLS_CC);
297 4 : bc_init_num(&second TSRMLS_CC);
298 4 : bc_init_num(&result TSRMLS_CC);
299 4 : php_str2num(&first, left TSRMLS_CC);
300 4 : php_str2num(&second, right TSRMLS_CC);
301 :
302 4 : bc_multiply(first, second, &result, scale TSRMLS_CC);
303 4 : if (result->n_scale > scale) {
304 0 : result->n_scale = scale;
305 : }
306 :
307 4 : RETVAL_STRING(bc_num2str(result), 0);
308 :
309 4 : bc_free_num(&first);
310 4 : bc_free_num(&second);
311 4 : bc_free_num(&result);
312 : }
313 : /* }}} */
314 :
315 : /* {{{ proto string bcdiv(string left_operand, string right_operand [, int scale]) U
316 : Returns the quotient of two arbitrary precision numbers (division) */
317 : PHP_FUNCTION(bcdiv)
318 6 : {
319 : char *left, *right;
320 : int left_len, right_len;
321 6 : long scale = BCG(bc_precision);
322 : bc_num first, second, result;
323 :
324 6 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale) == FAILURE) {
325 1 : return;
326 : }
327 :
328 5 : if ((int)scale < 0) {
329 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
330 0 : scale = 0;
331 : }
332 :
333 5 : bc_init_num(&first TSRMLS_CC);
334 5 : bc_init_num(&second TSRMLS_CC);
335 5 : bc_init_num(&result TSRMLS_CC);
336 5 : php_str2num(&first, left TSRMLS_CC);
337 5 : php_str2num(&second, right TSRMLS_CC);
338 :
339 5 : if (bc_divide(first, second, &result, scale TSRMLS_CC) == 0) {
340 4 : if (result->n_scale > scale) {
341 0 : result->n_scale = scale;
342 : }
343 4 : RETVAL_STRING(bc_num2str(result), 0);
344 : } else {
345 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Division by zero");
346 1 : RETVAL_NULL();
347 : }
348 :
349 5 : bc_free_num(&first);
350 5 : bc_free_num(&second);
351 5 : bc_free_num(&result);
352 : }
353 : /* }}} */
354 :
355 : /* {{{ proto string bcmod(string left_operand, string right_operand) U
356 : Returns the modulus of the two arbitrary precision operands */
357 : PHP_FUNCTION(bcmod)
358 4 : {
359 : char *left, *right;
360 : int left_len, right_len;
361 : bc_num first, second, result;
362 :
363 4 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &left, &left_len, &right, &right_len) == FAILURE) {
364 1 : return;
365 : }
366 :
367 3 : bc_init_num(&first TSRMLS_CC);
368 3 : bc_init_num(&second TSRMLS_CC);
369 3 : bc_init_num(&result TSRMLS_CC);
370 3 : php_str2num(&first, left TSRMLS_CC);
371 3 : php_str2num(&second, right TSRMLS_CC);
372 :
373 3 : if (bc_modulo(first, second, &result, 0 TSRMLS_CC) == 0) {
374 3 : RETVAL_STRING(bc_num2str(result), 0);
375 : } else {
376 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Division by zero");
377 0 : RETVAL_NULL();
378 : }
379 :
380 3 : bc_free_num(&first);
381 3 : bc_free_num(&second);
382 3 : bc_free_num(&result);
383 : }
384 : /* }}} */
385 :
386 : /* {{{ proto string bcpowmod(string x, string y, string mod [, int scale]) U
387 : Returns the value of an arbitrary precision number raised to the power of another reduced by a modulous */
388 : PHP_FUNCTION(bcpowmod)
389 3 : {
390 : char *left, *right, *modulous;
391 : int left_len, right_len, modulous_len;
392 : bc_num first, second, mod, result;
393 3 : long scale = BCG(bc_precision);
394 :
395 3 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|l", &left, &left_len, &right, &right_len, &modulous, &modulous_len, &scale) == FAILURE) {
396 3 : return;
397 : }
398 :
399 0 : if ((int)scale < 0) {
400 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
401 0 : scale = 0;
402 : }
403 :
404 0 : bc_init_num(&first TSRMLS_CC);
405 0 : bc_init_num(&second TSRMLS_CC);
406 0 : bc_init_num(&mod TSRMLS_CC);
407 0 : bc_init_num(&result TSRMLS_CC);
408 0 : php_str2num(&first, left TSRMLS_CC);
409 0 : php_str2num(&second, right TSRMLS_CC);
410 0 : php_str2num(&mod, modulous TSRMLS_CC);
411 0 : if (bc_raisemod(first, second, mod, &result, scale TSRMLS_CC) == 0) {
412 0 : if (result->n_scale > scale) {
413 0 : result->n_scale = scale;
414 : }
415 0 : RETVAL_STRING(bc_num2str(result), 0);
416 : } else {
417 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Division by zero");
418 0 : RETVAL_NULL();
419 : }
420 :
421 :
422 0 : bc_free_num(&first);
423 0 : bc_free_num(&second);
424 0 : bc_free_num(&mod);
425 0 : bc_free_num(&result);
426 : }
427 : /* }}} */
428 :
429 : /* {{{ proto string bcpow(string x, string y [, int scale]) U
430 : Returns the value of an arbitrary precision number raised to the power of another */
431 : PHP_FUNCTION(bcpow)
432 3 : {
433 : char *left, *right;
434 : int left_len, right_len;
435 3 : long scale = BCG(bc_precision);
436 : bc_num first, second, result;
437 :
438 3 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale) == FAILURE) {
439 0 : return;
440 : }
441 :
442 3 : if ((int)scale < 0) {
443 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
444 0 : scale = 0;
445 : }
446 :
447 3 : bc_init_num(&first TSRMLS_CC);
448 3 : bc_init_num(&second TSRMLS_CC);
449 3 : bc_init_num(&result TSRMLS_CC);
450 3 : php_str2num(&first, left TSRMLS_CC);
451 3 : php_str2num(&second, right TSRMLS_CC);
452 :
453 3 : bc_raise (first, second, &result, scale TSRMLS_CC);
454 3 : if (result->n_scale > scale) {
455 0 : result->n_scale = scale;
456 : }
457 3 : if (result->n_scale > scale) {
458 0 : result->n_scale = scale;
459 : }
460 3 : RETVAL_STRING(bc_num2str(result), 0);
461 :
462 3 : bc_free_num(&first);
463 3 : bc_free_num(&second);
464 3 : bc_free_num(&result);
465 : }
466 : /* }}} */
467 :
468 : /* {{{ proto string bcsqrt(string operand [, int scale]) U
469 : Returns the square root of an arbitray precision number */
470 : PHP_FUNCTION(bcsqrt)
471 3 : {
472 : char *operand;
473 : int operand_len;
474 3 : long scale = BCG(bc_precision);
475 : bc_num result;
476 :
477 3 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &operand, &operand_len, &scale) == FAILURE) {
478 0 : return;
479 : }
480 :
481 3 : if ((int)scale < 0) {
482 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
483 0 : scale = 0;
484 : }
485 :
486 3 : bc_init_num(&result TSRMLS_CC);
487 3 : php_str2num(&result, operand TSRMLS_CC);
488 3 : if (bc_sqrt (&result, scale TSRMLS_CC) != 0) {
489 2 : if (result->n_scale > scale) {
490 0 : result->n_scale = scale;
491 : }
492 2 : RETVAL_STRING(bc_num2str(result), 0);
493 : } else {
494 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Square root of negative number");
495 1 : RETVAL_NULL();
496 : }
497 3 : bc_free_num(&result);
498 : }
499 : /* }}} */
500 :
501 : /* {{{ proto int bccomp(string left_operand, string right_operand [, int scale]) U
502 : Compares two arbitrary precision numbers */
503 : PHP_FUNCTION(bccomp)
504 4 : {
505 : char *left, *right;
506 : int left_len, right_len;
507 4 : long scale = BCG(bc_precision);
508 : bc_num first, second;
509 :
510 4 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale) == FAILURE) {
511 0 : return;
512 : }
513 :
514 4 : if ((int)scale < 0) {
515 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
516 0 : scale = 0;
517 : }
518 :
519 4 : bc_init_num(&first TSRMLS_CC);
520 4 : bc_init_num(&second TSRMLS_CC);
521 :
522 4 : bc_str2num(&first, left, scale TSRMLS_CC);
523 4 : bc_str2num(&second, right, scale TSRMLS_CC);
524 :
525 4 : RETVAL_LONG(bc_compare(first, second));
526 :
527 4 : bc_free_num(&first);
528 4 : bc_free_num(&second);
529 : }
530 : /* }}} */
531 :
532 : /* {{{ proto bool bcscale(int scale) U
533 : Sets default scale parameter for all bc math functions */
534 : PHP_FUNCTION(bcscale)
535 3 : {
536 : long scale;
537 :
538 3 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &scale) == FAILURE) {
539 0 : return;
540 : }
541 :
542 3 : if ((int)scale < 0) {
543 0 : php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid scale given, using zero");
544 0 : RETURN_FALSE;
545 : }
546 :
547 3 : BCG(bc_precision) = scale;
548 :
549 3 : RETURN_TRUE;
550 : }
551 : /* }}} */
552 :
553 :
554 : #endif
555 :
556 : /*
557 : * Local variables:
558 : * tab-width: 4
559 : * c-basic-offset: 4
560 : * End:
561 : * vim600: sw=4 ts=4 fdm=marker
562 : * vim<600: sw=4 ts=4
563 : */
|