xref: /php-src/ext/bcmath/bcmath.c (revision cad0e555)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Andi Gutmans <andi@php.net>                                  |
14    +----------------------------------------------------------------------+
15 */
16 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include "php.h"
22 
23 #ifdef HAVE_BCMATH
24 
25 #include "php_ini.h"
26 #include "zend_exceptions.h"
27 #include "bcmath_arginfo.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 static PHP_MINIT_FUNCTION(bcmath);
36 static PHP_MSHUTDOWN_FUNCTION(bcmath);
37 static PHP_MINFO_FUNCTION(bcmath);
38 
39 zend_module_entry bcmath_module_entry = {
40 	STANDARD_MODULE_HEADER,
41 	"bcmath",
42 	ext_functions,
43 	PHP_MINIT(bcmath),
44 	PHP_MSHUTDOWN(bcmath),
45 	NULL,
46 	NULL,
47 	PHP_MINFO(bcmath),
48 	PHP_BCMATH_VERSION,
49 	PHP_MODULE_GLOBALS(bcmath),
50 	PHP_GINIT(bcmath),
51 	PHP_GSHUTDOWN(bcmath),
52 	NULL,
53 	STANDARD_MODULE_PROPERTIES_EX
54 };
55 
56 #ifdef COMPILE_DL_BCMATH
57 #ifdef ZTS
58 ZEND_TSRMLS_CACHE_DEFINE()
59 #endif
ZEND_GET_MODULE(bcmath)60 ZEND_GET_MODULE(bcmath)
61 #endif
62 
63 ZEND_INI_MH(OnUpdateScale)
64 {
65 	int *p;
66 	zend_long tmp;
67 
68 	tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
69 	if (tmp < 0 || tmp > INT_MAX) {
70 		return FAILURE;
71 	}
72 
73 	p = (int *) ZEND_INI_GET_ADDR();
74 	*p = (int) tmp;
75 
76 	return SUCCESS;
77 }
78 
79 /* {{{ PHP_INI */
80 PHP_INI_BEGIN()
81 	STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateScale, bc_precision, zend_bcmath_globals, bcmath_globals)
PHP_INI_END()82 PHP_INI_END()
83 /* }}} */
84 
85 /* {{{ PHP_GINIT_FUNCTION */
86 static PHP_GINIT_FUNCTION(bcmath)
87 {
88 #if defined(COMPILE_DL_BCMATH) && defined(ZTS)
89 	ZEND_TSRMLS_CACHE_UPDATE();
90 #endif
91 	bcmath_globals->bc_precision = 0;
92 	bc_init_numbers();
93 }
94 /* }}} */
95 
96 /* {{{ PHP_GSHUTDOWN_FUNCTION */
PHP_GSHUTDOWN_FUNCTION(bcmath)97 static PHP_GSHUTDOWN_FUNCTION(bcmath)
98 {
99 	_bc_free_num_ex(&bcmath_globals->_zero_, 1);
100 	_bc_free_num_ex(&bcmath_globals->_one_, 1);
101 	_bc_free_num_ex(&bcmath_globals->_two_, 1);
102 }
103 /* }}} */
104 
105 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(bcmath)106 PHP_MINIT_FUNCTION(bcmath)
107 {
108 	REGISTER_INI_ENTRIES();
109 
110 	return SUCCESS;
111 }
112 /* }}} */
113 
114 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(bcmath)115 PHP_MSHUTDOWN_FUNCTION(bcmath)
116 {
117 	UNREGISTER_INI_ENTRIES();
118 
119 	return SUCCESS;
120 }
121 /* }}} */
122 
123 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(bcmath)124 PHP_MINFO_FUNCTION(bcmath)
125 {
126 	php_info_print_table_start();
127 	php_info_print_table_row(2, "BCMath support", "enabled");
128 	php_info_print_table_end();
129 	DISPLAY_INI_ENTRIES();
130 }
131 /* }}} */
132 
133 /* {{{ php_str2num
134    Convert to bc_num detecting scale */
php_str2num(bc_num * num,const zend_string * str)135 static zend_result php_str2num(bc_num *num, const zend_string *str)
136 {
137 	if (!bc_str2num(num, ZSTR_VAL(str), ZSTR_VAL(str) + ZSTR_LEN(str), 0, true)) {
138 		return FAILURE;
139 	}
140 
141 	return SUCCESS;
142 }
143 /* }}} */
144 
145 /* {{{ Returns the sum of two arbitrary precision numbers */
PHP_FUNCTION(bcadd)146 PHP_FUNCTION(bcadd)
147 {
148 	zend_string *left, *right;
149 	zend_long scale_param;
150 	bool scale_param_is_null = 1;
151 	bc_num first = NULL, second = NULL, result = NULL;
152 	int scale;
153 
154 	ZEND_PARSE_PARAMETERS_START(2, 3)
155 		Z_PARAM_STR(left)
156 		Z_PARAM_STR(right)
157 		Z_PARAM_OPTIONAL
158 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
159 	ZEND_PARSE_PARAMETERS_END();
160 
161 	if (scale_param_is_null) {
162 		scale = BCG(bc_precision);
163 	} else if (scale_param < 0 || scale_param > INT_MAX) {
164 		zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
165 		RETURN_THROWS();
166 	} else {
167 		scale = (int) scale_param;
168 	}
169 
170 	if (php_str2num(&first, left) == FAILURE) {
171 		zend_argument_value_error(1, "is not well-formed");
172 		goto cleanup;
173 	}
174 
175 	if (php_str2num(&second, right) == FAILURE) {
176 		zend_argument_value_error(2, "is not well-formed");
177 		goto cleanup;
178 	}
179 
180 	result = bc_add (first, second, scale);
181 
182 	RETVAL_NEW_STR(bc_num2str_ex(result, scale));
183 
184 	cleanup: {
185 		bc_free_num(&first);
186 		bc_free_num(&second);
187 		bc_free_num(&result);
188 	};
189 }
190 /* }}} */
191 
192 /* {{{ Returns the difference between two arbitrary precision numbers */
PHP_FUNCTION(bcsub)193 PHP_FUNCTION(bcsub)
194 {
195 	zend_string *left, *right;
196 	zend_long scale_param;
197 	bool scale_param_is_null = 1;
198 	bc_num first = NULL, second = NULL, result = NULL;
199 	int scale;
200 
201 	ZEND_PARSE_PARAMETERS_START(2, 3)
202 		Z_PARAM_STR(left)
203 		Z_PARAM_STR(right)
204 		Z_PARAM_OPTIONAL
205 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
206 	ZEND_PARSE_PARAMETERS_END();
207 
208 	if (scale_param_is_null) {
209 		scale = BCG(bc_precision);
210 	} else if (scale_param < 0 || scale_param > INT_MAX) {
211 		zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
212 		RETURN_THROWS();
213 	} else {
214 		scale = (int) scale_param;
215 	}
216 
217 	if (php_str2num(&first, left) == FAILURE) {
218 		zend_argument_value_error(1, "is not well-formed");
219 		goto cleanup;
220 	}
221 
222 	if (php_str2num(&second, right) == FAILURE) {
223 		zend_argument_value_error(2, "is not well-formed");
224 		goto cleanup;
225 	}
226 
227 	result = bc_sub (first, second, scale);
228 
229 	RETVAL_NEW_STR(bc_num2str_ex(result, scale));
230 
231 	cleanup: {
232 		bc_free_num(&first);
233 		bc_free_num(&second);
234 		bc_free_num(&result);
235 	};
236 }
237 /* }}} */
238 
239 /* {{{ Returns the multiplication of two arbitrary precision numbers */
PHP_FUNCTION(bcmul)240 PHP_FUNCTION(bcmul)
241 {
242 	zend_string *left, *right;
243 	zend_long scale_param;
244 	bool scale_param_is_null = 1;
245 	bc_num first = NULL, second = NULL, result = NULL;
246 	int scale;
247 
248 	ZEND_PARSE_PARAMETERS_START(2, 3)
249 		Z_PARAM_STR(left)
250 		Z_PARAM_STR(right)
251 		Z_PARAM_OPTIONAL
252 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
253 	ZEND_PARSE_PARAMETERS_END();
254 
255 	if (scale_param_is_null) {
256 		scale = BCG(bc_precision);
257 	} else if (scale_param < 0 || scale_param > INT_MAX) {
258 		zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
259 		RETURN_THROWS();
260 	} else {
261 		scale = (int) scale_param;
262 	}
263 
264 	if (php_str2num(&first, left) == FAILURE) {
265 		zend_argument_value_error(1, "is not well-formed");
266 		goto cleanup;
267 	}
268 
269 	if (php_str2num(&second, right) == FAILURE) {
270 		zend_argument_value_error(2, "is not well-formed");
271 		goto cleanup;
272 	}
273 
274 	result = bc_multiply (first, second, scale);
275 
276 	RETVAL_NEW_STR(bc_num2str_ex(result, scale));
277 
278 	cleanup: {
279 		bc_free_num(&first);
280 		bc_free_num(&second);
281 		bc_free_num(&result);
282 	};
283 }
284 /* }}} */
285 
286 /* {{{ Returns the quotient of two arbitrary precision numbers (division) */
PHP_FUNCTION(bcdiv)287 PHP_FUNCTION(bcdiv)
288 {
289 	zend_string *left, *right;
290 	zend_long scale_param;
291 	bool scale_param_is_null = 1;
292 	bc_num first = NULL, second = NULL, result;
293 	int scale = BCG(bc_precision);
294 
295 	ZEND_PARSE_PARAMETERS_START(2, 3)
296 		Z_PARAM_STR(left)
297 		Z_PARAM_STR(right)
298 		Z_PARAM_OPTIONAL
299 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
300 	ZEND_PARSE_PARAMETERS_END();
301 
302 	if (scale_param_is_null) {
303 		scale = BCG(bc_precision);
304 	} else if (scale_param < 0 || scale_param > INT_MAX) {
305 		zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
306 		RETURN_THROWS();
307 	} else {
308 		scale = (int) scale_param;
309 	}
310 
311 	bc_init_num(&result);
312 
313 	if (php_str2num(&first, left) == FAILURE) {
314 		zend_argument_value_error(1, "is not well-formed");
315 		goto cleanup;
316 	}
317 
318 	if (php_str2num(&second, right) == FAILURE) {
319 		zend_argument_value_error(2, "is not well-formed");
320 		goto cleanup;
321 	}
322 
323 	if (!bc_divide(first, second, &result, scale)) {
324 		zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
325 		goto cleanup;
326 	}
327 
328 	RETVAL_NEW_STR(bc_num2str_ex(result, scale));
329 
330 	cleanup: {
331 		bc_free_num(&first);
332 		bc_free_num(&second);
333 		bc_free_num(&result);
334 	};
335 }
336 /* }}} */
337 
338 /* {{{ Returns the modulus of the two arbitrary precision operands */
PHP_FUNCTION(bcmod)339 PHP_FUNCTION(bcmod)
340 {
341 	zend_string *left, *right;
342 	zend_long scale_param;
343 	bool scale_param_is_null = 1;
344 	bc_num first = NULL, second = NULL, result;
345 	int scale = BCG(bc_precision);
346 
347 	ZEND_PARSE_PARAMETERS_START(2, 3)
348 		Z_PARAM_STR(left)
349 		Z_PARAM_STR(right)
350 		Z_PARAM_OPTIONAL
351 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
352 	ZEND_PARSE_PARAMETERS_END();
353 
354 	if (scale_param_is_null) {
355 		scale = BCG(bc_precision);
356 	} else if (scale_param < 0 || scale_param > INT_MAX) {
357 		zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
358 		RETURN_THROWS();
359 	} else {
360 		scale = (int) scale_param;
361 	}
362 
363 	bc_init_num(&result);
364 
365 	if (php_str2num(&first, left) == FAILURE) {
366 		zend_argument_value_error(1, "is not well-formed");
367 		goto cleanup;
368 	}
369 
370 	if (php_str2num(&second, right) == FAILURE) {
371 		zend_argument_value_error(2, "is not well-formed");
372 		goto cleanup;
373 	}
374 
375 	if (!bc_modulo(first, second, &result, scale)) {
376 		zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
377 		goto cleanup;
378 	}
379 
380 	RETVAL_NEW_STR(bc_num2str_ex(result, scale));
381 
382 	cleanup: {
383 		bc_free_num(&first);
384 		bc_free_num(&second);
385 		bc_free_num(&result);
386 	};
387 }
388 /* }}} */
389 
390 /* {{{ Returns the value of an arbitrary precision number raised to the power of another reduced by a modulus */
PHP_FUNCTION(bcpowmod)391 PHP_FUNCTION(bcpowmod)
392 {
393 	zend_string *base_str, *exponent_str, *modulus_str;
394 	zend_long scale_param;
395 	bool scale_param_is_null = 1;
396 	bc_num bc_base = NULL, bc_expo = NULL, bc_modulus = NULL, result;
397 	int scale = BCG(bc_precision);
398 
399 	ZEND_PARSE_PARAMETERS_START(3, 4)
400 		Z_PARAM_STR(base_str)
401 		Z_PARAM_STR(exponent_str)
402 		Z_PARAM_STR(modulus_str)
403 		Z_PARAM_OPTIONAL
404 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
405 	ZEND_PARSE_PARAMETERS_END();
406 
407 	if (scale_param_is_null) {
408 		scale = BCG(bc_precision);
409 	} else if (scale_param < 0 || scale_param > INT_MAX) {
410 		zend_argument_value_error(4, "must be between 0 and %d", INT_MAX);
411 		RETURN_THROWS();
412 	} else {
413 		scale = (int) scale_param;
414 	}
415 
416 	bc_init_num(&result);
417 
418 	if (php_str2num(&bc_base, base_str) == FAILURE) {
419 		zend_argument_value_error(1, "is not well-formed");
420 		goto cleanup;
421 	}
422 
423 	if (php_str2num(&bc_expo, exponent_str) == FAILURE) {
424 		zend_argument_value_error(2, "is not well-formed");
425 		goto cleanup;
426 	}
427 
428 	if (php_str2num(&bc_modulus, modulus_str) == FAILURE) {
429 		zend_argument_value_error(3, "is not well-formed");
430 		goto cleanup;
431 	}
432 
433 	raise_mod_status status = bc_raisemod(bc_base, bc_expo, bc_modulus, &result, scale);
434 	switch (status) {
435 		case BASE_HAS_FRACTIONAL:
436 			zend_argument_value_error(1, "cannot have a fractional part");
437 			goto cleanup;
438 		case EXPO_HAS_FRACTIONAL:
439 			zend_argument_value_error(2, "cannot have a fractional part");
440 			goto cleanup;
441 		case EXPO_IS_NEGATIVE:
442 			zend_argument_value_error(2, "must be greater than or equal to 0");
443 			goto cleanup;
444 		case MOD_HAS_FRACTIONAL:
445 			zend_argument_value_error(3, "cannot have a fractional part");
446 			goto cleanup;
447 		case MOD_IS_ZERO:
448 			zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
449 			goto cleanup;
450 		case OK:
451 			RETVAL_NEW_STR(bc_num2str_ex(result, scale));
452 			break;
453 		EMPTY_SWITCH_DEFAULT_CASE();
454 	}
455 
456 	cleanup: {
457 		bc_free_num(&bc_base);
458 		bc_free_num(&bc_expo);
459 		bc_free_num(&bc_modulus);
460 		bc_free_num(&result);
461 	};
462 }
463 /* }}} */
464 
465 /* {{{ Returns the value of an arbitrary precision number raised to the power of another */
PHP_FUNCTION(bcpow)466 PHP_FUNCTION(bcpow)
467 {
468 	zend_string *base_str, *exponent_str;
469 	zend_long scale_param;
470 	bool scale_param_is_null = 1;
471 	bc_num first = NULL, bc_exponent = NULL, result;
472 	int scale = BCG(bc_precision);
473 
474 	ZEND_PARSE_PARAMETERS_START(2, 3)
475 		Z_PARAM_STR(base_str)
476 		Z_PARAM_STR(exponent_str)
477 		Z_PARAM_OPTIONAL
478 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
479 	ZEND_PARSE_PARAMETERS_END();
480 
481 	if (scale_param_is_null) {
482 		scale = BCG(bc_precision);
483 	} else if (scale_param < 0 || scale_param > INT_MAX) {
484 		zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
485 		RETURN_THROWS();
486 	} else {
487 		scale = (int) scale_param;
488 	}
489 
490 	bc_init_num(&result);
491 
492 	if (php_str2num(&first, base_str) == FAILURE) {
493 		zend_argument_value_error(1, "is not well-formed");
494 		goto cleanup;
495 	}
496 
497 	if (php_str2num(&bc_exponent, exponent_str) == FAILURE) {
498 		zend_argument_value_error(2, "is not well-formed");
499 		goto cleanup;
500 	}
501 
502 	/* Check the exponent for scale digits and convert to a long. */
503 	if (bc_exponent->n_scale != 0) {
504 		zend_argument_value_error(2, "cannot have a fractional part");
505 		goto cleanup;
506 	}
507 	long exponent = bc_num2long(bc_exponent);
508 	if (exponent == 0 && (bc_exponent->n_len > 1 || bc_exponent->n_value[0] != 0)) {
509 		zend_argument_value_error(2, "is too large");
510 		goto cleanup;
511 	}
512 
513 	bc_raise(first, exponent, &result, scale);
514 
515 	RETVAL_NEW_STR(bc_num2str_ex(result, scale));
516 
517 	cleanup: {
518 		bc_free_num(&first);
519 		bc_free_num(&bc_exponent);
520 		bc_free_num(&result);
521 	};
522 }
523 /* }}} */
524 
525 /* {{{ Returns the square root of an arbitrary precision number */
PHP_FUNCTION(bcsqrt)526 PHP_FUNCTION(bcsqrt)
527 {
528 	zend_string *left;
529 	zend_long scale_param;
530 	bool scale_param_is_null = 1;
531 	bc_num result = NULL;
532 	int scale = BCG(bc_precision);
533 
534 	ZEND_PARSE_PARAMETERS_START(1, 2)
535 		Z_PARAM_STR(left)
536 		Z_PARAM_OPTIONAL
537 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
538 	ZEND_PARSE_PARAMETERS_END();
539 
540 	if (scale_param_is_null) {
541 		scale = BCG(bc_precision);
542 	} else if (scale_param < 0 || scale_param > INT_MAX) {
543 		zend_argument_value_error(2, "must be between 0 and %d", INT_MAX);
544 		RETURN_THROWS();
545 	} else {
546 		scale = (int) scale_param;
547 	}
548 
549 	if (php_str2num(&result, left) == FAILURE) {
550 		zend_argument_value_error(1, "is not well-formed");
551 		goto cleanup;
552 	}
553 
554 	if (bc_sqrt (&result, scale) != 0) {
555 		RETVAL_NEW_STR(bc_num2str_ex(result, scale));
556 	} else {
557 		zend_argument_value_error(1, "must be greater than or equal to 0");
558 	}
559 
560 	cleanup: {
561 		bc_free_num(&result);
562 	};
563 }
564 /* }}} */
565 
566 /* {{{ Compares two arbitrary precision numbers */
PHP_FUNCTION(bccomp)567 PHP_FUNCTION(bccomp)
568 {
569 	zend_string *left, *right;
570 	zend_long scale_param;
571 	bool scale_param_is_null = 1;
572 	bc_num first = NULL, second = NULL;
573 	int scale = BCG(bc_precision);
574 
575 	ZEND_PARSE_PARAMETERS_START(2, 3)
576 		Z_PARAM_STR(left)
577 		Z_PARAM_STR(right)
578 		Z_PARAM_OPTIONAL
579 		Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
580 	ZEND_PARSE_PARAMETERS_END();
581 
582 	if (scale_param_is_null) {
583 		scale = BCG(bc_precision);
584 	} else if (scale_param < 0 || scale_param > INT_MAX) {
585 		zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
586 		RETURN_THROWS();
587 	} else {
588 		scale = (int) scale_param;
589 	}
590 
591 	if (!bc_str2num(&first, ZSTR_VAL(left), ZSTR_VAL(left) + ZSTR_LEN(left), scale, false)) {
592 		zend_argument_value_error(1, "is not well-formed");
593 		goto cleanup;
594 	}
595 
596 	if (!bc_str2num(&second, ZSTR_VAL(right), ZSTR_VAL(right) + ZSTR_LEN(right), scale, false)) {
597 		zend_argument_value_error(2, "is not well-formed");
598 		goto cleanup;
599 	}
600 
601 	RETVAL_LONG(bc_compare(first, second));
602 
603 	cleanup: {
604 		bc_free_num(&first);
605 		bc_free_num(&second);
606 	};
607 }
608 /* }}} */
609 
610 /* {{{ floor or ceil */
bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAMETERS,bool is_floor)611 static void bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAMETERS, bool is_floor)
612 {
613 	zend_string *numstr;
614 	bc_num num = NULL, result = NULL;
615 
616 	ZEND_PARSE_PARAMETERS_START(1, 1)
617 		Z_PARAM_STR(numstr)
618 	ZEND_PARSE_PARAMETERS_END();
619 
620 	if (php_str2num(&num, numstr) == FAILURE) {
621 		zend_argument_value_error(1, "is not well-formed");
622 		goto cleanup;
623 	}
624 
625 	result = bc_floor_or_ceil(num, is_floor);
626 	RETVAL_NEW_STR(bc_num2str_ex(result, 0));
627 
628 	cleanup: {
629 		bc_free_num(&num);
630 		bc_free_num(&result);
631 	};
632 }
633 /* }}} */
634 
635 /* {{{ Returns floor of num */
PHP_FUNCTION(bcfloor)636 PHP_FUNCTION(bcfloor)
637 {
638 	bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
639 }
640 /* }}} */
641 
642 /* {{{ Returns ceil of num */
PHP_FUNCTION(bcceil)643 PHP_FUNCTION(bcceil)
644 {
645 	bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
646 }
647 /* }}} */
648 
649 /* {{{ Returns num rounded to the digits specified by precision. */
PHP_FUNCTION(bcround)650 PHP_FUNCTION(bcround)
651 {
652 	zend_string *numstr;
653 	zend_long precision = 0;
654 	zend_long mode = PHP_ROUND_HALF_UP;
655 	bc_num num = NULL, result;
656 
657 	ZEND_PARSE_PARAMETERS_START(1, 3)
658 		Z_PARAM_STR(numstr)
659 		Z_PARAM_OPTIONAL
660 		Z_PARAM_LONG(precision)
661 		Z_PARAM_LONG(mode)
662 	ZEND_PARSE_PARAMETERS_END();
663 
664 	switch (mode) {
665 		case PHP_ROUND_HALF_UP:
666 		case PHP_ROUND_HALF_DOWN:
667 		case PHP_ROUND_HALF_EVEN:
668 		case PHP_ROUND_HALF_ODD:
669 		case PHP_ROUND_CEILING:
670 		case PHP_ROUND_FLOOR:
671 		case PHP_ROUND_TOWARD_ZERO:
672 		case PHP_ROUND_AWAY_FROM_ZERO:
673 			break;
674 		default:
675 			zend_argument_value_error(3, "must be a valid rounding mode (PHP_ROUND_*)");
676 			return;
677 	}
678 
679 	bc_init_num(&result);
680 
681 	if (php_str2num(&num, numstr) == FAILURE) {
682 		zend_argument_value_error(1, "is not well-formed");
683 		goto cleanup;
684 	}
685 
686 	bc_round(num, precision, mode, &result);
687 	RETVAL_NEW_STR(bc_num2str_ex(result, result->n_scale));
688 
689 	cleanup: {
690 		bc_free_num(&num);
691 		bc_free_num(&result);
692 	};
693 }
694 /* }}} */
695 
696 /* {{{ Sets default scale parameter for all bc math functions */
PHP_FUNCTION(bcscale)697 PHP_FUNCTION(bcscale)
698 {
699 	zend_long old_scale, new_scale;
700 	bool new_scale_is_null = 1;
701 
702 	ZEND_PARSE_PARAMETERS_START(0, 1)
703 		Z_PARAM_OPTIONAL
704 		Z_PARAM_LONG_OR_NULL(new_scale, new_scale_is_null)
705 	ZEND_PARSE_PARAMETERS_END();
706 
707 	old_scale = BCG(bc_precision);
708 
709 	if (!new_scale_is_null) {
710 		if (new_scale < 0 || new_scale > INT_MAX) {
711 			zend_argument_value_error(1, "must be between 0 and %d", INT_MAX);
712 			RETURN_THROWS();
713 		}
714 
715 		zend_string *ini_name = ZSTR_INIT_LITERAL("bcmath.scale", 0);
716 		zend_string *new_scale_str = zend_long_to_str(new_scale);
717 		zend_alter_ini_entry(ini_name, new_scale_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
718 		zend_string_release(new_scale_str);
719 		zend_string_release(ini_name);
720 	}
721 
722 	RETURN_LONG(old_scale);
723 }
724 /* }}} */
725 
726 
727 #endif
728