xref: /PHP-7.2/ext/bcmath/bcmath.c (revision 7a7ec01a)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 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_EX(arginfo_bcmod, 0, 0, 2)
64 	ZEND_ARG_INFO(0, left_operand)
65 	ZEND_ARG_INFO(0, right_operand)
66 	ZEND_ARG_INFO(0, scale)
67 ZEND_END_ARG_INFO()
68 
69 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcpowmod, 0, 0, 3)
70 	ZEND_ARG_INFO(0, x)
71 	ZEND_ARG_INFO(0, y)
72 	ZEND_ARG_INFO(0, mod)
73 	ZEND_ARG_INFO(0, scale)
74 ZEND_END_ARG_INFO()
75 
76 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcpow, 0, 0, 2)
77 	ZEND_ARG_INFO(0, x)
78 	ZEND_ARG_INFO(0, y)
79 	ZEND_ARG_INFO(0, scale)
80 ZEND_END_ARG_INFO()
81 
82 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcsqrt, 0, 0, 1)
83 	ZEND_ARG_INFO(0, operand)
84 	ZEND_ARG_INFO(0, scale)
85 ZEND_END_ARG_INFO()
86 
87 ZEND_BEGIN_ARG_INFO_EX(arginfo_bccomp, 0, 0, 2)
88 	ZEND_ARG_INFO(0, left_operand)
89 	ZEND_ARG_INFO(0, right_operand)
90 	ZEND_ARG_INFO(0, scale)
91 ZEND_END_ARG_INFO()
92 
93 ZEND_BEGIN_ARG_INFO(arginfo_bcscale, 0)
94 	ZEND_ARG_INFO(0, scale)
95 ZEND_END_ARG_INFO()
96 
97 /* }}} */
98 
99 const zend_function_entry bcmath_functions[] = {
100 	PHP_FE(bcadd,									arginfo_bcadd)
101 	PHP_FE(bcsub,									arginfo_bcsub)
102 	PHP_FE(bcmul,									arginfo_bcmul)
103 	PHP_FE(bcdiv,									arginfo_bcdiv)
104 	PHP_FE(bcmod,									arginfo_bcmod)
105 	PHP_FE(bcpow,									arginfo_bcpow)
106 	PHP_FE(bcsqrt,									arginfo_bcsqrt)
107 	PHP_FE(bcscale,									arginfo_bcscale)
108 	PHP_FE(bccomp,									arginfo_bccomp)
109 	PHP_FE(bcpowmod,								arginfo_bcpowmod)
110 	PHP_FE_END
111 };
112 
113 zend_module_entry bcmath_module_entry = {
114 	STANDARD_MODULE_HEADER,
115 	"bcmath",
116 	bcmath_functions,
117 	PHP_MINIT(bcmath),
118 	PHP_MSHUTDOWN(bcmath),
119 	NULL,
120 	NULL,
121 	PHP_MINFO(bcmath),
122 	PHP_BCMATH_VERSION,
123 	PHP_MODULE_GLOBALS(bcmath),
124 	PHP_GINIT(bcmath),
125     PHP_GSHUTDOWN(bcmath),
126 	NULL,
127 	STANDARD_MODULE_PROPERTIES_EX
128 };
129 
130 #ifdef COMPILE_DL_BCMATH
131 #ifdef ZTS
132 ZEND_TSRMLS_CACHE_DEFINE()
133 #endif
ZEND_GET_MODULE(bcmath)134 ZEND_GET_MODULE(bcmath)
135 #endif
136 
137 /* {{{ PHP_INI */
138 PHP_INI_BEGIN()
139 	STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateLongGEZero, bc_precision, zend_bcmath_globals, bcmath_globals)
140 PHP_INI_END()
141 /* }}} */
142 
143 /* {{{ PHP_GINIT_FUNCTION
144  */
145 static PHP_GINIT_FUNCTION(bcmath)
146 {
147 #if defined(COMPILE_DL_BCMATH) && defined(ZTS)
148 	ZEND_TSRMLS_CACHE_UPDATE();
149 #endif
150 	bcmath_globals->bc_precision = 0;
151 	bc_init_numbers();
152 }
153 /* }}} */
154 
155 /* {{{ PHP_GSHUTDOWN_FUNCTION
156  */
PHP_GSHUTDOWN_FUNCTION(bcmath)157 static PHP_GSHUTDOWN_FUNCTION(bcmath)
158 {
159 	_bc_free_num_ex(&bcmath_globals->_zero_, 1);
160 	_bc_free_num_ex(&bcmath_globals->_one_, 1);
161 	_bc_free_num_ex(&bcmath_globals->_two_, 1);
162 }
163 /* }}} */
164 
165 /* {{{ PHP_MINIT_FUNCTION
166  */
PHP_MINIT_FUNCTION(bcmath)167 PHP_MINIT_FUNCTION(bcmath)
168 {
169 	REGISTER_INI_ENTRIES();
170 
171 	return SUCCESS;
172 }
173 /* }}} */
174 
175 /* {{{ PHP_MSHUTDOWN_FUNCTION
176  */
PHP_MSHUTDOWN_FUNCTION(bcmath)177 PHP_MSHUTDOWN_FUNCTION(bcmath)
178 {
179 	UNREGISTER_INI_ENTRIES();
180 
181 	return SUCCESS;
182 }
183 /* }}} */
184 
185 /* {{{ PHP_MINFO_FUNCTION
186  */
PHP_MINFO_FUNCTION(bcmath)187 PHP_MINFO_FUNCTION(bcmath)
188 {
189 	php_info_print_table_start();
190 	php_info_print_table_row(2, "BCMath support", "enabled");
191 	php_info_print_table_end();
192 	DISPLAY_INI_ENTRIES();
193 }
194 /* }}} */
195 
196 /* {{{ php_str2num
197    Convert to bc_num detecting scale */
php_str2num(bc_num * num,char * str)198 static void php_str2num(bc_num *num, char *str)
199 {
200 	char *p;
201 
202 	if (!(p = strchr(str, '.'))) {
203 		bc_str2num(num, str, 0);
204 		return;
205 	}
206 
207 	bc_str2num(num, str, strlen(p+1));
208 }
209 /* }}} */
210 
211 /* {{{ split_bc_num
212    Convert to bc_num detecting scale */
split_bc_num(bc_num num)213 static bc_num split_bc_num(bc_num num) {
214 	bc_num newnum;
215 	if (num->n_refs >= 1) {
216 		return num;
217 	}
218 	newnum = _bc_new_num_ex(0, 0, 0);
219 	*newnum = *num;
220 	newnum->n_refs = 1;
221 	num->n_refs--;
222 	return newnum;
223 }
224 /* }}} */
225 
226 /* {{{ proto string bcadd(string left_operand, string right_operand [, int scale])
227    Returns the sum of two arbitrary precision numbers */
PHP_FUNCTION(bcadd)228 PHP_FUNCTION(bcadd)
229 {
230 	zend_string *left, *right;
231 	zend_long scale_param = 0;
232 	bc_num first, second, result;
233 	int scale = (int)BCG(bc_precision);
234 
235 	ZEND_PARSE_PARAMETERS_START(2, 3)
236 		Z_PARAM_STR(left)
237 		Z_PARAM_STR(right)
238 		Z_PARAM_OPTIONAL
239 		Z_PARAM_LONG(scale_param)
240 	ZEND_PARSE_PARAMETERS_END();
241 
242 	if (ZEND_NUM_ARGS() == 3) {
243 		scale = (int) (scale_param < 0 ? 0 : scale_param);
244 	}
245 
246 	bc_init_num(&first);
247 	bc_init_num(&second);
248 	bc_init_num(&result);
249 	php_str2num(&first, ZSTR_VAL(left));
250 	php_str2num(&second, ZSTR_VAL(right));
251 	bc_add (first, second, &result, scale);
252 
253 	if (result->n_scale > scale) {
254 		result = split_bc_num(result);
255 		result->n_scale = scale;
256 	}
257 
258 	RETVAL_STR(bc_num2str(result));
259 	bc_free_num(&first);
260 	bc_free_num(&second);
261 	bc_free_num(&result);
262 	return;
263 }
264 /* }}} */
265 
266 /* {{{ proto string bcsub(string left_operand, string right_operand [, int scale])
267    Returns the difference between two arbitrary precision numbers */
PHP_FUNCTION(bcsub)268 PHP_FUNCTION(bcsub)
269 {
270 	zend_string *left, *right;
271 	zend_long scale_param = 0;
272 	bc_num first, second, result;
273 	int scale = (int)BCG(bc_precision);
274 
275 	ZEND_PARSE_PARAMETERS_START(2, 3)
276 		Z_PARAM_STR(left)
277 		Z_PARAM_STR(right)
278 		Z_PARAM_OPTIONAL
279 		Z_PARAM_LONG(scale_param)
280 	ZEND_PARSE_PARAMETERS_END();
281 
282 	if (ZEND_NUM_ARGS() == 3) {
283 		scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
284 	}
285 
286 	bc_init_num(&first);
287 	bc_init_num(&second);
288 	bc_init_num(&result);
289 	php_str2num(&first, ZSTR_VAL(left));
290 	php_str2num(&second, ZSTR_VAL(right));
291 	bc_sub (first, second, &result, scale);
292 
293 	if (result->n_scale > scale) {
294 		result = split_bc_num(result);
295 		result->n_scale = scale;
296 	}
297 
298 	RETVAL_STR(bc_num2str(result));
299 	bc_free_num(&first);
300 	bc_free_num(&second);
301 	bc_free_num(&result);
302 	return;
303 }
304 /* }}} */
305 
306 /* {{{ proto string bcmul(string left_operand, string right_operand [, int scale])
307    Returns the multiplication of two arbitrary precision numbers */
PHP_FUNCTION(bcmul)308 PHP_FUNCTION(bcmul)
309 {
310 	zend_string *left, *right;
311 	zend_long scale_param = 0;
312 	bc_num first, second, result;
313 	int scale = (int)BCG(bc_precision);
314 
315 	ZEND_PARSE_PARAMETERS_START(2, 3)
316 		Z_PARAM_STR(left)
317 		Z_PARAM_STR(right)
318 		Z_PARAM_OPTIONAL
319 		Z_PARAM_LONG(scale_param)
320 	ZEND_PARSE_PARAMETERS_END();
321 
322 	if (ZEND_NUM_ARGS() == 3) {
323 		scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
324 	}
325 
326 	bc_init_num(&first);
327 	bc_init_num(&second);
328 	bc_init_num(&result);
329 	php_str2num(&first, ZSTR_VAL(left));
330 	php_str2num(&second, ZSTR_VAL(right));
331 	bc_multiply (first, second, &result, scale);
332 
333 	if (result->n_scale > scale) {
334 		result = split_bc_num(result);
335 		result->n_scale = scale;
336 	}
337 
338 	RETVAL_STR(bc_num2str(result));
339 	bc_free_num(&first);
340 	bc_free_num(&second);
341 	bc_free_num(&result);
342 	return;
343 }
344 /* }}} */
345 
346 /* {{{ proto string bcdiv(string left_operand, string right_operand [, int scale])
347    Returns the quotient of two arbitrary precision numbers (division) */
PHP_FUNCTION(bcdiv)348 PHP_FUNCTION(bcdiv)
349 {
350 	zend_string *left, *right;
351 	zend_long scale_param = 0;
352 	bc_num first, second, result;
353 	int scale = (int)BCG(bc_precision);
354 
355 	ZEND_PARSE_PARAMETERS_START(2, 3)
356 		Z_PARAM_STR(left)
357 		Z_PARAM_STR(right)
358 		Z_PARAM_OPTIONAL
359 		Z_PARAM_LONG(scale_param)
360 	ZEND_PARSE_PARAMETERS_END();
361 
362 	if (ZEND_NUM_ARGS() == 3) {
363 		scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
364 	}
365 
366 	bc_init_num(&first);
367 	bc_init_num(&second);
368 	bc_init_num(&result);
369 	php_str2num(&first, ZSTR_VAL(left));
370 	php_str2num(&second, ZSTR_VAL(right));
371 
372 	switch (bc_divide(first, second, &result, scale)) {
373 		case 0: /* OK */
374 			if (result->n_scale > scale) {
375 				result = split_bc_num(result);
376 				result->n_scale = scale;
377 			}
378 			RETVAL_STR(bc_num2str(result));
379 			break;
380 		case -1: /* division by zero */
381 			php_error_docref(NULL, E_WARNING, "Division by zero");
382 			break;
383 	}
384 
385 	bc_free_num(&first);
386 	bc_free_num(&second);
387 	bc_free_num(&result);
388 	return;
389 }
390 /* }}} */
391 
392 /* {{{ proto string bcmod(string left_operand, string right_operand [, int scale])
393    Returns the modulus of the two arbitrary precision operands */
PHP_FUNCTION(bcmod)394 PHP_FUNCTION(bcmod)
395 {
396 	zend_string *left, *right;
397 	zend_long scale_param = 0;
398 	bc_num first, second, result;
399 	int scale = (int)BCG(bc_precision);
400 
401 	ZEND_PARSE_PARAMETERS_START(2, 3)
402 		Z_PARAM_STR(left)
403 		Z_PARAM_STR(right)
404 		Z_PARAM_OPTIONAL
405 		Z_PARAM_LONG(scale_param)
406 	ZEND_PARSE_PARAMETERS_END();
407 
408 	if (ZEND_NUM_ARGS() == 3) {
409 		scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
410 	}
411 
412 	bc_init_num(&first);
413 	bc_init_num(&second);
414 	bc_init_num(&result);
415 	php_str2num(&first, ZSTR_VAL(left));
416 	php_str2num(&second, ZSTR_VAL(right));
417 
418 	switch (bc_modulo(first, second, &result, scale)) {
419 		case 0:
420 			if (result->n_scale > scale) {
421 				result = split_bc_num(result);
422 				result->n_scale = scale;
423 			}
424 			RETVAL_STR(bc_num2str(result));
425 			break;
426 		case -1:
427 			php_error_docref(NULL, E_WARNING, "Division by zero");
428 			break;
429 	}
430 
431 	bc_free_num(&first);
432 	bc_free_num(&second);
433 	bc_free_num(&result);
434 	return;
435 }
436 /* }}} */
437 
438 /* {{{ proto string bcpowmod(string x, string y, string mod [, int scale])
439    Returns the value of an arbitrary precision number raised to the power of another reduced by a modulous */
PHP_FUNCTION(bcpowmod)440 PHP_FUNCTION(bcpowmod)
441 {
442 	zend_string *left, *right, *modulous;
443 	bc_num first, second, mod, result;
444 	zend_long scale = BCG(bc_precision);
445 	int scale_int;
446 
447 	ZEND_PARSE_PARAMETERS_START(3, 4)
448 		Z_PARAM_STR(left)
449 		Z_PARAM_STR(right)
450 		Z_PARAM_STR(modulous)
451 		Z_PARAM_OPTIONAL
452 		Z_PARAM_LONG(scale)
453 	ZEND_PARSE_PARAMETERS_END();
454 
455 	bc_init_num(&first);
456 	bc_init_num(&second);
457 	bc_init_num(&mod);
458 	bc_init_num(&result);
459 	php_str2num(&first, ZSTR_VAL(left));
460 	php_str2num(&second, ZSTR_VAL(right));
461 	php_str2num(&mod, ZSTR_VAL(modulous));
462 
463 	scale_int = (int) ((int)scale < 0 ? 0 : scale);
464 
465 	if (bc_raisemod(first, second, mod, &result, scale_int) != -1) {
466 		if (result->n_scale > scale_int) {
467 			result = split_bc_num(result);
468 			result->n_scale = scale_int;
469 		}
470 		RETVAL_STR(bc_num2str(result));
471 	} else {
472 		RETVAL_FALSE;
473 	}
474 
475 	bc_free_num(&first);
476 	bc_free_num(&second);
477 	bc_free_num(&mod);
478 	bc_free_num(&result);
479 	return;
480 }
481 /* }}} */
482 
483 /* {{{ proto string bcpow(string x, string y [, int scale])
484    Returns the value of an arbitrary precision number raised to the power of another */
PHP_FUNCTION(bcpow)485 PHP_FUNCTION(bcpow)
486 {
487 	zend_string *left, *right;
488 	zend_long scale_param = 0;
489 	bc_num first, second, result;
490 	int scale = (int)BCG(bc_precision);
491 
492 	ZEND_PARSE_PARAMETERS_START(2, 3)
493 		Z_PARAM_STR(left)
494 		Z_PARAM_STR(right)
495 		Z_PARAM_OPTIONAL
496 		Z_PARAM_LONG(scale_param)
497 	ZEND_PARSE_PARAMETERS_END();
498 
499 	if (ZEND_NUM_ARGS() == 3) {
500 		scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
501 	}
502 
503 	bc_init_num(&first);
504 	bc_init_num(&second);
505 	bc_init_num(&result);
506 	php_str2num(&first, ZSTR_VAL(left));
507 	php_str2num(&second, ZSTR_VAL(right));
508 	bc_raise (first, second, &result, scale);
509 
510 	if (result->n_scale > scale) {
511 		result = split_bc_num(result);
512 		result->n_scale = scale;
513 	}
514 
515 	RETVAL_STR(bc_num2str(result));
516 	bc_free_num(&first);
517 	bc_free_num(&second);
518 	bc_free_num(&result);
519 	return;
520 }
521 /* }}} */
522 
523 /* {{{ proto string bcsqrt(string operand [, int scale])
524    Returns the square root of an arbitray precision number */
PHP_FUNCTION(bcsqrt)525 PHP_FUNCTION(bcsqrt)
526 {
527 	zend_string *left;
528 	zend_long scale_param = 0;
529 	bc_num result;
530 	int scale = (int)BCG(bc_precision);
531 
532 	ZEND_PARSE_PARAMETERS_START(1, 2)
533 		Z_PARAM_STR(left)
534 		Z_PARAM_OPTIONAL
535 		Z_PARAM_LONG(scale_param)
536 	ZEND_PARSE_PARAMETERS_END();
537 
538 	if (ZEND_NUM_ARGS() == 2) {
539 		scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
540 	}
541 
542 	bc_init_num(&result);
543 	php_str2num(&result, ZSTR_VAL(left));
544 
545 	if (bc_sqrt (&result, scale) != 0) {
546 		if (result->n_scale > scale) {
547 			result = split_bc_num(result);
548 			result->n_scale = scale;
549 		}
550 		RETVAL_STR(bc_num2str(result));
551 	} else {
552 		php_error_docref(NULL, E_WARNING, "Square root of negative number");
553 	}
554 
555 	bc_free_num(&result);
556 	return;
557 }
558 /* }}} */
559 
560 /* {{{ proto int bccomp(string left_operand, string right_operand [, int scale])
561    Compares two arbitrary precision numbers */
PHP_FUNCTION(bccomp)562 PHP_FUNCTION(bccomp)
563 {
564 	zend_string *left, *right;
565 	zend_long scale_param = 0;
566 	bc_num first, second;
567 	int scale = (int)BCG(bc_precision);
568 
569 	ZEND_PARSE_PARAMETERS_START(2, 3)
570 		Z_PARAM_STR(left)
571 		Z_PARAM_STR(right)
572 		Z_PARAM_OPTIONAL
573 		Z_PARAM_LONG(scale_param)
574 	ZEND_PARSE_PARAMETERS_END();
575 
576 	if (ZEND_NUM_ARGS() == 3) {
577 		scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
578 	}
579 
580 	bc_init_num(&first);
581 	bc_init_num(&second);
582 
583 	bc_str2num(&first, ZSTR_VAL(left), scale);
584 	bc_str2num(&second, ZSTR_VAL(right), scale);
585 	RETVAL_LONG(bc_compare(first, second));
586 
587 	bc_free_num(&first);
588 	bc_free_num(&second);
589 	return;
590 }
591 /* }}} */
592 
593 /* {{{ proto bool bcscale(int scale)
594    Sets default scale parameter for all bc math functions */
PHP_FUNCTION(bcscale)595 PHP_FUNCTION(bcscale)
596 {
597 	zend_long new_scale;
598 
599 	ZEND_PARSE_PARAMETERS_START(1, 1)
600 		Z_PARAM_LONG(new_scale)
601 	ZEND_PARSE_PARAMETERS_END();
602 
603 	BCG(bc_precision) = ((int)new_scale < 0) ? 0 : new_scale;
604 
605 	RETURN_TRUE;
606 }
607 /* }}} */
608 
609 
610 #endif
611 
612 /*
613  * Local variables:
614  * tab-width: 4
615  * c-basic-offset: 4
616  * End:
617  * vim600: sw=4 ts=4 fdm=marker
618  * vim<600: sw=4 ts=4
619  */
620