bcmath.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  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. #ifdef HAVE_CONFIG_H
  17. #include "config.h"
  18. #endif
  19. #include "php.h"
  20. #ifdef HAVE_BCMATH
  21. #include "php_ini.h"
  22. #include "zend_exceptions.h"
  23. #include "bcmath_arginfo.h"
  24. #include "ext/standard/info.h"
  25. #include "php_bcmath.h"
  26. #include "libbcmath/src/bcmath.h"
  27. ZEND_DECLARE_MODULE_GLOBALS(bcmath)
  28. static PHP_GINIT_FUNCTION(bcmath);
  29. static PHP_GSHUTDOWN_FUNCTION(bcmath);
  30. zend_module_entry bcmath_module_entry = {
  31. STANDARD_MODULE_HEADER,
  32. "bcmath",
  33. ext_functions,
  34. PHP_MINIT(bcmath),
  35. PHP_MSHUTDOWN(bcmath),
  36. NULL,
  37. NULL,
  38. PHP_MINFO(bcmath),
  39. PHP_BCMATH_VERSION,
  40. PHP_MODULE_GLOBALS(bcmath),
  41. PHP_GINIT(bcmath),
  42. PHP_GSHUTDOWN(bcmath),
  43. NULL,
  44. STANDARD_MODULE_PROPERTIES_EX
  45. };
  46. #ifdef COMPILE_DL_BCMATH
  47. #ifdef ZTS
  48. ZEND_TSRMLS_CACHE_DEFINE()
  49. #endif
  50. ZEND_GET_MODULE(bcmath)
  51. #endif
  52. ZEND_INI_MH(OnUpdateScale)
  53. {
  54. int *p;
  55. zend_long tmp;
  56. tmp = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
  57. if (tmp < 0 || tmp > INT_MAX) {
  58. return FAILURE;
  59. }
  60. p = (int *) ZEND_INI_GET_ADDR();
  61. *p = (int) tmp;
  62. return SUCCESS;
  63. }
  64. /* {{{ PHP_INI */
  65. PHP_INI_BEGIN()
  66. STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateScale, bc_precision, zend_bcmath_globals, bcmath_globals)
  67. PHP_INI_END()
  68. /* }}} */
  69. /* {{{ PHP_GINIT_FUNCTION */
  70. static PHP_GINIT_FUNCTION(bcmath)
  71. {
  72. #if defined(COMPILE_DL_BCMATH) && defined(ZTS)
  73. ZEND_TSRMLS_CACHE_UPDATE();
  74. #endif
  75. bcmath_globals->bc_precision = 0;
  76. bc_init_numbers();
  77. }
  78. /* }}} */
  79. /* {{{ PHP_GSHUTDOWN_FUNCTION */
  80. static PHP_GSHUTDOWN_FUNCTION(bcmath)
  81. {
  82. _bc_free_num_ex(&bcmath_globals->_zero_, 1);
  83. _bc_free_num_ex(&bcmath_globals->_one_, 1);
  84. _bc_free_num_ex(&bcmath_globals->_two_, 1);
  85. }
  86. /* }}} */
  87. /* {{{ PHP_MINIT_FUNCTION */
  88. PHP_MINIT_FUNCTION(bcmath)
  89. {
  90. REGISTER_INI_ENTRIES();
  91. return SUCCESS;
  92. }
  93. /* }}} */
  94. /* {{{ PHP_MSHUTDOWN_FUNCTION */
  95. PHP_MSHUTDOWN_FUNCTION(bcmath)
  96. {
  97. UNREGISTER_INI_ENTRIES();
  98. return SUCCESS;
  99. }
  100. /* }}} */
  101. /* {{{ PHP_MINFO_FUNCTION */
  102. PHP_MINFO_FUNCTION(bcmath)
  103. {
  104. php_info_print_table_start();
  105. php_info_print_table_row(2, "BCMath support", "enabled");
  106. php_info_print_table_end();
  107. DISPLAY_INI_ENTRIES();
  108. }
  109. /* }}} */
  110. /* {{{ php_str2num
  111. Convert to bc_num detecting scale */
  112. static zend_result php_str2num(bc_num *num, char *str)
  113. {
  114. char *p;
  115. if (!(p = strchr(str, '.'))) {
  116. if (!bc_str2num(num, str, 0)) {
  117. return FAILURE;
  118. }
  119. return SUCCESS;
  120. }
  121. if (!bc_str2num(num, str, strlen(p+1))) {
  122. return FAILURE;
  123. }
  124. return SUCCESS;
  125. }
  126. /* }}} */
  127. /* {{{ Returns the sum of two arbitrary precision numbers */
  128. PHP_FUNCTION(bcadd)
  129. {
  130. zend_string *left, *right;
  131. zend_long scale_param;
  132. bool scale_param_is_null = 1;
  133. bc_num first, second, result;
  134. int scale;
  135. ZEND_PARSE_PARAMETERS_START(2, 3)
  136. Z_PARAM_STR(left)
  137. Z_PARAM_STR(right)
  138. Z_PARAM_OPTIONAL
  139. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  140. ZEND_PARSE_PARAMETERS_END();
  141. if (scale_param_is_null) {
  142. scale = BCG(bc_precision);
  143. } else if (scale_param < 0 || scale_param > INT_MAX) {
  144. zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
  145. RETURN_THROWS();
  146. } else {
  147. scale = (int) scale_param;
  148. }
  149. bc_init_num(&first);
  150. bc_init_num(&second);
  151. bc_init_num(&result);
  152. if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
  153. zend_argument_value_error(1, "is not well-formed");
  154. goto cleanup;
  155. }
  156. if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
  157. zend_argument_value_error(2, "is not well-formed");
  158. goto cleanup;
  159. }
  160. bc_add (first, second, &result, scale);
  161. RETVAL_STR(bc_num2str_ex(result, scale));
  162. cleanup: {
  163. bc_free_num(&first);
  164. bc_free_num(&second);
  165. bc_free_num(&result);
  166. };
  167. }
  168. /* }}} */
  169. /* {{{ Returns the difference between two arbitrary precision numbers */
  170. PHP_FUNCTION(bcsub)
  171. {
  172. zend_string *left, *right;
  173. zend_long scale_param;
  174. bool scale_param_is_null = 1;
  175. bc_num first, second, result;
  176. int scale;
  177. ZEND_PARSE_PARAMETERS_START(2, 3)
  178. Z_PARAM_STR(left)
  179. Z_PARAM_STR(right)
  180. Z_PARAM_OPTIONAL
  181. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  182. ZEND_PARSE_PARAMETERS_END();
  183. if (scale_param_is_null) {
  184. scale = BCG(bc_precision);
  185. } else if (scale_param < 0 || scale_param > INT_MAX) {
  186. zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
  187. RETURN_THROWS();
  188. } else {
  189. scale = (int) scale_param;
  190. }
  191. bc_init_num(&first);
  192. bc_init_num(&second);
  193. bc_init_num(&result);
  194. if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
  195. zend_argument_value_error(1, "is not well-formed");
  196. goto cleanup;
  197. }
  198. if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
  199. zend_argument_value_error(2, "is not well-formed");
  200. goto cleanup;
  201. }
  202. bc_sub (first, second, &result, scale);
  203. RETVAL_STR(bc_num2str_ex(result, scale));
  204. cleanup: {
  205. bc_free_num(&first);
  206. bc_free_num(&second);
  207. bc_free_num(&result);
  208. };
  209. }
  210. /* }}} */
  211. /* {{{ Returns the multiplication of two arbitrary precision numbers */
  212. PHP_FUNCTION(bcmul)
  213. {
  214. zend_string *left, *right;
  215. zend_long scale_param;
  216. bool scale_param_is_null = 1;
  217. bc_num first, second, result;
  218. int scale;
  219. ZEND_PARSE_PARAMETERS_START(2, 3)
  220. Z_PARAM_STR(left)
  221. Z_PARAM_STR(right)
  222. Z_PARAM_OPTIONAL
  223. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  224. ZEND_PARSE_PARAMETERS_END();
  225. if (scale_param_is_null) {
  226. scale = BCG(bc_precision);
  227. } else if (scale_param < 0 || scale_param > INT_MAX) {
  228. zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
  229. RETURN_THROWS();
  230. } else {
  231. scale = (int) scale_param;
  232. }
  233. bc_init_num(&first);
  234. bc_init_num(&second);
  235. bc_init_num(&result);
  236. if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
  237. zend_argument_value_error(1, "is not well-formed");
  238. goto cleanup;
  239. }
  240. if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
  241. zend_argument_value_error(2, "is not well-formed");
  242. goto cleanup;
  243. }
  244. bc_multiply (first, second, &result, scale);
  245. RETVAL_STR(bc_num2str_ex(result, scale));
  246. cleanup: {
  247. bc_free_num(&first);
  248. bc_free_num(&second);
  249. bc_free_num(&result);
  250. };
  251. }
  252. /* }}} */
  253. /* {{{ Returns the quotient of two arbitrary precision numbers (division) */
  254. PHP_FUNCTION(bcdiv)
  255. {
  256. zend_string *left, *right;
  257. zend_long scale_param;
  258. bool scale_param_is_null = 1;
  259. bc_num first, second, result;
  260. int scale = BCG(bc_precision);
  261. ZEND_PARSE_PARAMETERS_START(2, 3)
  262. Z_PARAM_STR(left)
  263. Z_PARAM_STR(right)
  264. Z_PARAM_OPTIONAL
  265. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  266. ZEND_PARSE_PARAMETERS_END();
  267. if (scale_param_is_null) {
  268. scale = BCG(bc_precision);
  269. } else if (scale_param < 0 || scale_param > INT_MAX) {
  270. zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
  271. RETURN_THROWS();
  272. } else {
  273. scale = (int) scale_param;
  274. }
  275. bc_init_num(&first);
  276. bc_init_num(&second);
  277. bc_init_num(&result);
  278. if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
  279. zend_argument_value_error(1, "is not well-formed");
  280. goto cleanup;
  281. }
  282. if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
  283. zend_argument_value_error(2, "is not well-formed");
  284. goto cleanup;
  285. }
  286. switch (bc_divide(first, second, &result, scale)) {
  287. case 0: /* OK */
  288. RETVAL_STR(bc_num2str_ex(result, scale));
  289. break;
  290. case -1: /* division by zero */
  291. zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
  292. break;
  293. }
  294. cleanup: {
  295. bc_free_num(&first);
  296. bc_free_num(&second);
  297. bc_free_num(&result);
  298. };
  299. }
  300. /* }}} */
  301. /* {{{ Returns the modulus of the two arbitrary precision operands */
  302. PHP_FUNCTION(bcmod)
  303. {
  304. zend_string *left, *right;
  305. zend_long scale_param;
  306. bool scale_param_is_null = 1;
  307. bc_num first, second, result;
  308. int scale = BCG(bc_precision);
  309. ZEND_PARSE_PARAMETERS_START(2, 3)
  310. Z_PARAM_STR(left)
  311. Z_PARAM_STR(right)
  312. Z_PARAM_OPTIONAL
  313. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  314. ZEND_PARSE_PARAMETERS_END();
  315. if (scale_param_is_null) {
  316. scale = BCG(bc_precision);
  317. } else if (scale_param < 0 || scale_param > INT_MAX) {
  318. zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
  319. RETURN_THROWS();
  320. } else {
  321. scale = (int) scale_param;
  322. }
  323. bc_init_num(&first);
  324. bc_init_num(&second);
  325. bc_init_num(&result);
  326. if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
  327. zend_argument_value_error(1, "is not well-formed");
  328. goto cleanup;
  329. }
  330. if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
  331. zend_argument_value_error(2, "is not well-formed");
  332. goto cleanup;
  333. }
  334. switch (bc_modulo(first, second, &result, scale)) {
  335. case 0:
  336. RETVAL_STR(bc_num2str_ex(result, scale));
  337. break;
  338. case -1:
  339. zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
  340. break;
  341. }
  342. cleanup: {
  343. bc_free_num(&first);
  344. bc_free_num(&second);
  345. bc_free_num(&result);
  346. };
  347. }
  348. /* }}} */
  349. /* {{{ Returns the value of an arbitrary precision number raised to the power of another reduced by a modulus */
  350. PHP_FUNCTION(bcpowmod)
  351. {
  352. zend_string *left, *right, *modulus;
  353. zend_long scale_param;
  354. bool scale_param_is_null = 1;
  355. bc_num first, second, mod, result;
  356. int scale = BCG(bc_precision);
  357. ZEND_PARSE_PARAMETERS_START(3, 4)
  358. Z_PARAM_STR(left)
  359. Z_PARAM_STR(right)
  360. Z_PARAM_STR(modulus)
  361. Z_PARAM_OPTIONAL
  362. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  363. ZEND_PARSE_PARAMETERS_END();
  364. if (scale_param_is_null) {
  365. scale = BCG(bc_precision);
  366. } else if (scale_param < 0 || scale_param > INT_MAX) {
  367. zend_argument_value_error(4, "must be between 0 and %d", INT_MAX);
  368. RETURN_THROWS();
  369. } else {
  370. scale = (int) scale_param;
  371. }
  372. bc_init_num(&first);
  373. bc_init_num(&second);
  374. bc_init_num(&mod);
  375. bc_init_num(&result);
  376. if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
  377. zend_argument_value_error(1, "is not well-formed");
  378. goto cleanup;
  379. }
  380. if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
  381. zend_argument_value_error(2, "is not well-formed");
  382. goto cleanup;
  383. }
  384. if (php_str2num(&mod, ZSTR_VAL(modulus)) == FAILURE) {
  385. zend_argument_value_error(3, "is not well-formed");
  386. goto cleanup;
  387. }
  388. if (bc_raisemod(first, second, mod, &result, scale) == SUCCESS) {
  389. RETVAL_STR(bc_num2str_ex(result, scale));
  390. }
  391. cleanup: {
  392. bc_free_num(&first);
  393. bc_free_num(&second);
  394. bc_free_num(&mod);
  395. bc_free_num(&result);
  396. };
  397. }
  398. /* }}} */
  399. /* {{{ Returns the value of an arbitrary precision number raised to the power of another */
  400. PHP_FUNCTION(bcpow)
  401. {
  402. zend_string *left, *right;
  403. zend_long scale_param;
  404. bool scale_param_is_null = 1;
  405. bc_num first, second, result;
  406. int scale = BCG(bc_precision);
  407. ZEND_PARSE_PARAMETERS_START(2, 3)
  408. Z_PARAM_STR(left)
  409. Z_PARAM_STR(right)
  410. Z_PARAM_OPTIONAL
  411. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  412. ZEND_PARSE_PARAMETERS_END();
  413. if (scale_param_is_null) {
  414. scale = BCG(bc_precision);
  415. } else if (scale_param < 0 || scale_param > INT_MAX) {
  416. zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
  417. RETURN_THROWS();
  418. } else {
  419. scale = (int) scale_param;
  420. }
  421. bc_init_num(&first);
  422. bc_init_num(&second);
  423. bc_init_num(&result);
  424. if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
  425. zend_argument_value_error(1, "is not well-formed");
  426. goto cleanup;
  427. }
  428. if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
  429. zend_argument_value_error(2, "is not well-formed");
  430. goto cleanup;
  431. }
  432. bc_raise (first, second, &result, scale);
  433. RETVAL_STR(bc_num2str_ex(result, scale));
  434. cleanup: {
  435. bc_free_num(&first);
  436. bc_free_num(&second);
  437. bc_free_num(&result);
  438. };
  439. }
  440. /* }}} */
  441. /* {{{ Returns the square root of an arbitrary precision number */
  442. PHP_FUNCTION(bcsqrt)
  443. {
  444. zend_string *left;
  445. zend_long scale_param;
  446. bool scale_param_is_null = 1;
  447. bc_num result;
  448. int scale = BCG(bc_precision);
  449. ZEND_PARSE_PARAMETERS_START(1, 2)
  450. Z_PARAM_STR(left)
  451. Z_PARAM_OPTIONAL
  452. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  453. ZEND_PARSE_PARAMETERS_END();
  454. if (scale_param_is_null) {
  455. scale = BCG(bc_precision);
  456. } else if (scale_param < 0 || scale_param > INT_MAX) {
  457. zend_argument_value_error(2, "must be between 0 and %d", INT_MAX);
  458. RETURN_THROWS();
  459. } else {
  460. scale = (int) scale_param;
  461. }
  462. bc_init_num(&result);
  463. if (php_str2num(&result, ZSTR_VAL(left)) == FAILURE) {
  464. zend_argument_value_error(1, "is not well-formed");
  465. goto cleanup;
  466. }
  467. if (bc_sqrt (&result, scale) != 0) {
  468. RETVAL_STR(bc_num2str_ex(result, scale));
  469. } else {
  470. zend_argument_value_error(1, "must be greater than or equal to 0");
  471. }
  472. cleanup: {
  473. bc_free_num(&result);
  474. };
  475. }
  476. /* }}} */
  477. /* {{{ Compares two arbitrary precision numbers */
  478. PHP_FUNCTION(bccomp)
  479. {
  480. zend_string *left, *right;
  481. zend_long scale_param;
  482. bool scale_param_is_null = 1;
  483. bc_num first, second;
  484. int scale = BCG(bc_precision);
  485. ZEND_PARSE_PARAMETERS_START(2, 3)
  486. Z_PARAM_STR(left)
  487. Z_PARAM_STR(right)
  488. Z_PARAM_OPTIONAL
  489. Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
  490. ZEND_PARSE_PARAMETERS_END();
  491. if (scale_param_is_null) {
  492. scale = BCG(bc_precision);
  493. } else if (scale_param < 0 || scale_param > INT_MAX) {
  494. zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
  495. RETURN_THROWS();
  496. } else {
  497. scale = (int) scale_param;
  498. }
  499. bc_init_num(&first);
  500. bc_init_num(&second);
  501. if (!bc_str2num(&first, ZSTR_VAL(left), scale)) {
  502. zend_argument_value_error(1, "is not well-formed");
  503. goto cleanup;
  504. }
  505. if (!bc_str2num(&second, ZSTR_VAL(right), scale)) {
  506. zend_argument_value_error(2, "is not well-formed");
  507. goto cleanup;
  508. }
  509. RETVAL_LONG(bc_compare(first, second));
  510. cleanup: {
  511. bc_free_num(&first);
  512. bc_free_num(&second);
  513. };
  514. }
  515. /* }}} */
  516. /* {{{ Sets default scale parameter for all bc math functions */
  517. PHP_FUNCTION(bcscale)
  518. {
  519. zend_long old_scale, new_scale;
  520. bool new_scale_is_null = 1;
  521. ZEND_PARSE_PARAMETERS_START(0, 1)
  522. Z_PARAM_OPTIONAL
  523. Z_PARAM_LONG_OR_NULL(new_scale, new_scale_is_null)
  524. ZEND_PARSE_PARAMETERS_END();
  525. old_scale = BCG(bc_precision);
  526. if (!new_scale_is_null) {
  527. if (new_scale < 0 || new_scale > INT_MAX) {
  528. zend_argument_value_error(1, "must be between 0 and %d", INT_MAX);
  529. RETURN_THROWS();
  530. }
  531. zend_string *ini_name = zend_string_init("bcmath.scale", sizeof("bcmath.scale") - 1, 0);
  532. zend_string *new_scale_str = zend_long_to_str(new_scale);
  533. zend_alter_ini_entry(ini_name, new_scale_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
  534. zend_string_release(new_scale_str);
  535. zend_string_release(ini_name);
  536. }
  537. RETURN_LONG(old_scale);
  538. }
  539. /* }}} */
  540. #endif