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