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(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