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