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