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