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