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