xref: /PHP-5.4/ext/bcmath/bcmath.c (revision c0d060f5)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2014 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$ */
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 	PHP_FE_END
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 */
PHP_INI_BEGIN()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 {
143 	bcmath_globals->bc_precision = 0;
144 	bc_init_numbers(TSRMLS_C);
145 }
146 /* }}} */
147 
148 /* {{{ PHP_GSHUTDOWN_FUNCTION
149  */
PHP_GSHUTDOWN_FUNCTION(bcmath)150 static PHP_GSHUTDOWN_FUNCTION(bcmath)
151 {
152 	_bc_free_num_ex(&bcmath_globals->_zero_, 1);
153 	_bc_free_num_ex(&bcmath_globals->_one_, 1);
154 	_bc_free_num_ex(&bcmath_globals->_two_, 1);
155 }
156 /* }}} */
157 
158 /* {{{ PHP_MINIT_FUNCTION
159  */
PHP_MINIT_FUNCTION(bcmath)160 PHP_MINIT_FUNCTION(bcmath)
161 {
162 	REGISTER_INI_ENTRIES();
163 
164 	return SUCCESS;
165 }
166 /* }}} */
167 
168 /* {{{ PHP_MSHUTDOWN_FUNCTION
169  */
PHP_MSHUTDOWN_FUNCTION(bcmath)170 PHP_MSHUTDOWN_FUNCTION(bcmath)
171 {
172 	UNREGISTER_INI_ENTRIES();
173 
174 	return SUCCESS;
175 }
176 /* }}} */
177 
178 /* {{{ PHP_MINFO_FUNCTION
179  */
PHP_MINFO_FUNCTION(bcmath)180 PHP_MINFO_FUNCTION(bcmath)
181 {
182 	php_info_print_table_start();
183 	php_info_print_table_row(2, "BCMath support", "enabled");
184 	php_info_print_table_end();
185 	DISPLAY_INI_ENTRIES();
186 }
187 /* }}} */
188 
189 /* {{{ php_str2num
190    Convert to bc_num detecting scale */
php_str2num(bc_num * num,char * str TSRMLS_DC)191 static void php_str2num(bc_num *num, char *str TSRMLS_DC)
192 {
193 	char *p;
194 
195 	if (!(p = strchr(str, '.'))) {
196 		bc_str2num(num, str, 0 TSRMLS_CC);
197 		return;
198 	}
199 
200 	bc_str2num(num, str, strlen(p+1) TSRMLS_CC);
201 }
202 /* }}} */
203 
204 /* {{{ proto string bcadd(string left_operand, string right_operand [, int scale])
205    Returns the sum of two arbitrary precision numbers */
PHP_FUNCTION(bcadd)206 PHP_FUNCTION(bcadd)
207 {
208 	char *left, *right;
209 	long scale_param = 0;
210 	bc_num first, second, result;
211 	int left_len, right_len;
212 	int scale = BCG(bc_precision), argc = ZEND_NUM_ARGS();
213 
214 	if (zend_parse_parameters(argc TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale_param) == FAILURE) {
215 		return;
216 	}
217 
218 	if (argc == 3) {
219 		scale = (int) ((int)scale_param < 0) ? 0 : scale_param;
220 	}
221 
222 	bc_init_num(&first TSRMLS_CC);
223 	bc_init_num(&second TSRMLS_CC);
224 	bc_init_num(&result TSRMLS_CC);
225 	php_str2num(&first, left TSRMLS_CC);
226 	php_str2num(&second, right TSRMLS_CC);
227 	bc_add (first, second, &result, scale);
228 
229 	if (result->n_scale > scale) {
230 		result->n_scale = scale;
231 	}
232 
233 	Z_STRVAL_P(return_value) = bc_num2str(result);
234 	Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
235 	Z_TYPE_P(return_value) = IS_STRING;
236 	bc_free_num(&first);
237 	bc_free_num(&second);
238 	bc_free_num(&result);
239 	return;
240 }
241 /* }}} */
242 
243 /* {{{ proto string bcsub(string left_operand, string right_operand [, int scale])
244    Returns the difference between two arbitrary precision numbers */
PHP_FUNCTION(bcsub)245 PHP_FUNCTION(bcsub)
246 {
247 	char *left, *right;
248 	int left_len, right_len;
249 	long scale_param = 0;
250 	bc_num first, second, result;
251 	int scale = BCG(bc_precision), argc = ZEND_NUM_ARGS();
252 
253 	if (zend_parse_parameters(argc TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale_param) == FAILURE) {
254 		return;
255 	}
256 
257 	if (argc == 3) {
258 		scale = (int) ((int)scale_param < 0) ? 0 : scale_param;
259 	}
260 
261 	bc_init_num(&first TSRMLS_CC);
262 	bc_init_num(&second TSRMLS_CC);
263 	bc_init_num(&result TSRMLS_CC);
264 	php_str2num(&first, left TSRMLS_CC);
265 	php_str2num(&second, right TSRMLS_CC);
266 	bc_sub (first, second, &result, scale);
267 
268 	if (result->n_scale > scale) {
269 		result->n_scale = scale;
270 	}
271 
272 	Z_STRVAL_P(return_value) = bc_num2str(result);
273 	Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
274 	Z_TYPE_P(return_value) = IS_STRING;
275 	bc_free_num(&first);
276 	bc_free_num(&second);
277 	bc_free_num(&result);
278 	return;
279 }
280 /* }}} */
281 
282 /* {{{ proto string bcmul(string left_operand, string right_operand [, int scale])
283    Returns the multiplication of two arbitrary precision numbers */
PHP_FUNCTION(bcmul)284 PHP_FUNCTION(bcmul)
285 {
286 	char *left, *right;
287 	int left_len, right_len;
288 	long scale_param = 0;
289 	bc_num first, second, result;
290 	int scale = BCG(bc_precision), argc = ZEND_NUM_ARGS();
291 
292 	if (zend_parse_parameters(argc TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale_param) == FAILURE) {
293 		return;
294 	}
295 
296 	if (argc == 3) {
297 		scale = (int) ((int)scale_param < 0) ? 0 : scale_param;
298 	}
299 
300 	bc_init_num(&first TSRMLS_CC);
301 	bc_init_num(&second TSRMLS_CC);
302 	bc_init_num(&result TSRMLS_CC);
303 	php_str2num(&first, left TSRMLS_CC);
304 	php_str2num(&second, right TSRMLS_CC);
305 	bc_multiply (first, second, &result, scale TSRMLS_CC);
306 
307 	if (result->n_scale > scale) {
308 		result->n_scale = scale;
309 	}
310 
311 	Z_STRVAL_P(return_value) = bc_num2str(result);
312 	Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
313 	Z_TYPE_P(return_value) = IS_STRING;
314 	bc_free_num(&first);
315 	bc_free_num(&second);
316 	bc_free_num(&result);
317 	return;
318 }
319 /* }}} */
320 
321 /* {{{ proto string bcdiv(string left_operand, string right_operand [, int scale])
322    Returns the quotient of two arbitrary precision numbers (division) */
PHP_FUNCTION(bcdiv)323 PHP_FUNCTION(bcdiv)
324 {
325 	char *left, *right;
326 	int left_len, right_len;
327 	long scale_param = 0;
328 	bc_num first, second, result;
329 	int scale = BCG(bc_precision), argc = ZEND_NUM_ARGS();
330 
331 	if (zend_parse_parameters(argc TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale_param) == FAILURE) {
332 		return;
333 	}
334 
335 	if (argc == 3) {
336 		scale = (int) ((int)scale_param < 0) ? 0 : scale_param;
337 	}
338 
339 	bc_init_num(&first TSRMLS_CC);
340 	bc_init_num(&second TSRMLS_CC);
341 	bc_init_num(&result TSRMLS_CC);
342 	php_str2num(&first, left TSRMLS_CC);
343 	php_str2num(&second, right TSRMLS_CC);
344 
345 	switch (bc_divide(first, second, &result, scale TSRMLS_CC)) {
346 		case 0: /* OK */
347 			if (result->n_scale > scale) {
348 				result->n_scale = scale;
349 			}
350 			Z_STRVAL_P(return_value) = bc_num2str(result);
351 			Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
352 			Z_TYPE_P(return_value) = IS_STRING;
353 			break;
354 		case -1: /* division by zero */
355 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Division by zero");
356 			break;
357 	}
358 
359 	bc_free_num(&first);
360 	bc_free_num(&second);
361 	bc_free_num(&result);
362 	return;
363 }
364 /* }}} */
365 
366 /* {{{ proto string bcmod(string left_operand, string right_operand)
367    Returns the modulus of the two arbitrary precision operands */
PHP_FUNCTION(bcmod)368 PHP_FUNCTION(bcmod)
369 {
370 	char *left, *right;
371 	int left_len, right_len;
372 	bc_num first, second, result;
373 
374 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &left, &left_len, &right, &right_len) == FAILURE) {
375 		return;
376 	}
377 
378 	bc_init_num(&first TSRMLS_CC);
379 	bc_init_num(&second TSRMLS_CC);
380 	bc_init_num(&result TSRMLS_CC);
381 	bc_str2num(&first, left, 0 TSRMLS_CC);
382 	bc_str2num(&second, right, 0 TSRMLS_CC);
383 
384 	switch (bc_modulo(first, second, &result, 0 TSRMLS_CC)) {
385 		case 0:
386 			Z_STRVAL_P(return_value) = bc_num2str(result);
387 			Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
388 			Z_TYPE_P(return_value) = IS_STRING;
389 			break;
390 		case -1:
391 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Division by zero");
392 			break;
393 	}
394 
395 	bc_free_num(&first);
396 	bc_free_num(&second);
397 	bc_free_num(&result);
398 	return;
399 }
400 /* }}} */
401 
402 /* {{{ proto string bcpowmod(string x, string y, string mod [, int scale])
403    Returns the value of an arbitrary precision number raised to the power of another reduced by a modulous */
PHP_FUNCTION(bcpowmod)404 PHP_FUNCTION(bcpowmod)
405 {
406 	char *left, *right, *modulous;
407 	int left_len, right_len, modulous_len;
408 	bc_num first, second, mod, result;
409 	long scale = BCG(bc_precision);
410 	int scale_int;
411 
412 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|l", &left, &left_len, &right, &right_len, &modulous, &modulous_len, &scale) == FAILURE) {
413 		return;
414 	}
415 
416 	bc_init_num(&first TSRMLS_CC);
417 	bc_init_num(&second TSRMLS_CC);
418 	bc_init_num(&mod TSRMLS_CC);
419 	bc_init_num(&result TSRMLS_CC);
420 	php_str2num(&first, left TSRMLS_CC);
421 	php_str2num(&second, right TSRMLS_CC);
422 	php_str2num(&mod, modulous TSRMLS_CC);
423 
424 	scale_int = (int) ((int)scale < 0) ? 0 : scale;
425 
426 	if (bc_raisemod(first, second, mod, &result, scale_int TSRMLS_CC) != -1) {
427 		if (result->n_scale > scale) {
428 			result->n_scale = scale;
429 		}
430 		Z_STRVAL_P(return_value) = bc_num2str(result);
431 		Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
432 		Z_TYPE_P(return_value) = IS_STRING;
433 	} else {
434 		RETVAL_FALSE;
435 	}
436 
437 	bc_free_num(&first);
438 	bc_free_num(&second);
439 	bc_free_num(&mod);
440 	bc_free_num(&result);
441 	return;
442 }
443 /* }}} */
444 
445 /* {{{ proto string bcpow(string x, string y [, int scale])
446    Returns the value of an arbitrary precision number raised to the power of another */
PHP_FUNCTION(bcpow)447 PHP_FUNCTION(bcpow)
448 {
449 	char *left, *right;
450 	int left_len, right_len;
451 	long scale_param = 0;
452 	bc_num first, second, result;
453 	int scale = BCG(bc_precision), argc = ZEND_NUM_ARGS();
454 
455 	if (zend_parse_parameters(argc TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale_param) == FAILURE) {
456 		return;
457 	}
458 
459 	if (argc == 3) {
460 		scale = (int) ((int)scale_param < 0) ? 0 : scale_param;
461 	}
462 
463 	bc_init_num(&first TSRMLS_CC);
464 	bc_init_num(&second TSRMLS_CC);
465 	bc_init_num(&result TSRMLS_CC);
466 	php_str2num(&first, left TSRMLS_CC);
467 	php_str2num(&second, right TSRMLS_CC);
468 	bc_raise (first, second, &result, scale TSRMLS_CC);
469 
470 	if (result->n_scale > scale) {
471 		result->n_scale = scale;
472 	}
473 
474 	Z_STRVAL_P(return_value) = bc_num2str(result);
475 	Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
476 	Z_TYPE_P(return_value) = IS_STRING;
477 	bc_free_num(&first);
478 	bc_free_num(&second);
479 	bc_free_num(&result);
480 	return;
481 }
482 /* }}} */
483 
484 /* {{{ proto string bcsqrt(string operand [, int scale])
485    Returns the square root of an arbitray precision number */
PHP_FUNCTION(bcsqrt)486 PHP_FUNCTION(bcsqrt)
487 {
488 	char *left;
489 	int left_len;
490 	long scale_param = 0;
491 	bc_num result;
492 	int scale = BCG(bc_precision), argc = ZEND_NUM_ARGS();
493 
494 	if (zend_parse_parameters(argc TSRMLS_CC, "s|l", &left, &left_len, &scale_param) == FAILURE) {
495 		return;
496 	}
497 
498 	if (argc == 2) {
499 		scale = (int) ((int)scale_param < 0) ? 0 : scale_param;
500 	}
501 
502 	bc_init_num(&result TSRMLS_CC);
503 	php_str2num(&result, left TSRMLS_CC);
504 
505 	if (bc_sqrt (&result, scale TSRMLS_CC) != 0) {
506 		if (result->n_scale > scale) {
507 			result->n_scale = scale;
508 		}
509 		Z_STRVAL_P(return_value) = bc_num2str(result);
510 		Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
511 		Z_TYPE_P(return_value) = IS_STRING;
512 	} else {
513 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Square root of negative number");
514 	}
515 
516 	bc_free_num(&result);
517 	return;
518 }
519 /* }}} */
520 
521 /* {{{ proto int bccomp(string left_operand, string right_operand [, int scale])
522    Compares two arbitrary precision numbers */
PHP_FUNCTION(bccomp)523 PHP_FUNCTION(bccomp)
524 {
525 	char *left, *right;
526 	int left_len, right_len;
527 	long scale_param = 0;
528 	bc_num first, second;
529 	int scale = BCG(bc_precision), argc = ZEND_NUM_ARGS();
530 
531 	if (zend_parse_parameters(argc TSRMLS_CC, "ss|l", &left, &left_len, &right, &right_len, &scale_param) == FAILURE) {
532 		return;
533 	}
534 
535 	if (argc == 3) {
536 		scale = (int) ((int)scale_param < 0) ? 0 : scale_param;
537 	}
538 
539 	bc_init_num(&first TSRMLS_CC);
540 	bc_init_num(&second TSRMLS_CC);
541 
542 	bc_str2num(&first, left, scale TSRMLS_CC);
543 	bc_str2num(&second, right, scale TSRMLS_CC);
544 	Z_LVAL_P(return_value) = bc_compare(first, second);
545 	Z_TYPE_P(return_value) = IS_LONG;
546 
547 	bc_free_num(&first);
548 	bc_free_num(&second);
549 	return;
550 }
551 /* }}} */
552 
553 /* {{{ proto bool bcscale(int scale)
554    Sets default scale parameter for all bc math functions */
PHP_FUNCTION(bcscale)555 PHP_FUNCTION(bcscale)
556 {
557 	long new_scale;
558 
559 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &new_scale) == FAILURE) {
560 		return;
561 	}
562 
563 	BCG(bc_precision) = ((int)new_scale < 0) ? 0 : new_scale;
564 
565 	RETURN_TRUE;
566 }
567 /* }}} */
568 
569 
570 #endif
571 
572 /*
573  * Local variables:
574  * tab-width: 4
575  * c-basic-offset: 4
576  * End:
577  * vim600: sw=4 ts=4 fdm=marker
578  * vim<600: sw=4 ts=4
579  */
580