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