zh_calc.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. 'use strict';
  2. /**
  3. * 1. 必须引用decimal.min.js
  4. * 2. 如果使用ZhCalc.mathCalcExpr和ZhCalc.tranExpr必须引用math.min.js
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. ;
  10. const zhBaseCalc = (function () {
  11. const zeroPrecision = 12, mulPrecision = 12, divPrecision = 12;
  12. function digitLength (num) {
  13. // 兼容科学计数
  14. var eSplit = num.toString().split(/[eE]/);
  15. var len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0));
  16. return len > 0 ? len : 0;
  17. }
  18. function powLength (num) {
  19. var rs = num.toString();
  20. if (rs.indexOf('+') > 0) {
  21. return rs.match(/0*$/g).length();
  22. } else {
  23. const eSplit = rs.split(/[eE]/);
  24. const len = Number(eSplit[1]) - this.digitLength(eSplit[0]);
  25. return len > 0 ? len : 0;
  26. }
  27. }
  28. function round (num, digit) {
  29. return Math.round(num * Math.pow(10, digit)) / Math.pow(10, digit);
  30. }
  31. function add(num1, num2) {
  32. var d1 = this.digitLength(num1), d2 = this.digitLength(num2);
  33. return this.round(num1 + num2, Math.max(d1, d2));
  34. }
  35. function sub(num1, num2) {
  36. var d1 = this.digitLength(num1), d2 = this.digitLength(num2);
  37. return this.round(num1 - num2, Math.max(d1, d2));
  38. }
  39. function mul(num1, num2) {
  40. return this.round(num1 * num2, mulPrecision);
  41. }
  42. function div(num1, num2) {
  43. return this.round(num1 / num2, divPrecision);
  44. }
  45. function isNonZero(num) {
  46. return num && round(num, zeroPrecision) !== 0;
  47. }
  48. return {
  49. digitLength: digitLength,
  50. powLength: powLength,
  51. round: round,
  52. add: add, sub: sub, mul: mul, div: div,
  53. isNonZero: isNonZero,
  54. }
  55. })();
  56. /**
  57. * 计算(四则、舍入) 统一,方便以后置换
  58. * @type {{add, sub, mul, div, round}}
  59. */
  60. const ZhCalc = (function () {
  61. let mathConfig = false;
  62. Decimal.set({precision: 50, defaults: true});
  63. const percentReg = /((\d+)|((\d+)(\.\d+)))%/g;
  64. /**
  65. * 加法 num1 + num2
  66. * @param num1
  67. * @param num2
  68. * @returns {number}
  69. */
  70. function add(num1, num2) {
  71. //return zhBaseCalc.add(num1 ? num1 : 0, num2 ? num2: 0);
  72. return num1 ? (num2 ? zhBaseCalc.add(num1, num2) : num1) : num2;
  73. };
  74. function sum(array) {
  75. let result = 0;
  76. for (const a of array) {
  77. result = ZhCalc.add(result, a);
  78. }
  79. return result;
  80. }
  81. /**
  82. * 减法 num1 - num2
  83. * @param num1
  84. * @param num2
  85. * @returns {number}
  86. */
  87. function sub(num1, num2) {
  88. return zhBaseCalc.sub(num1 ? num1 : 0, num2 ? num2 : 0);
  89. }
  90. /**
  91. * 乘法 num1 * num2
  92. * @param num1
  93. * @param num2
  94. * @returns {*}
  95. */
  96. function mul(num1, num2, digit = 6) {
  97. //return Decimal.mul(num1 ? num1 : 0, num2 ? num2 : 0).toDecimalPlaces(digit).toNumber();
  98. return (num1 && num2) ? (Decimal.mul(num1, num2).toDecimalPlaces(digit).toNumber()) : 0;
  99. }
  100. /**
  101. * 除法 num1 / num2
  102. * @param num1 - 被除数
  103. * @param num2 - 除数
  104. * @returns {*}
  105. */
  106. function div(num1, num2, digit = 6) {
  107. if (num2 && !checkZero(num2)) {
  108. //return Decimal.div(num1 ? num1: 0, num2).toDecimalPlaces(digit).toNumber();
  109. return num1 ? (Decimal.div(num1, num2).toDecimalPlaces(digit).toNumber()) : 0;
  110. } else {
  111. return null;
  112. }
  113. }
  114. /**
  115. * 四舍五入
  116. * @param {Number} value - 舍入的数字
  117. * @param {Number} decimal - 要保留的小数位数
  118. * @returns {*}
  119. */
  120. function round(value, decimal) {
  121. decimal = decimal ? parseInt(decimal) : 0;
  122. return value ? new Decimal(value).toDecimalPlaces(decimal).toNumber() : null;
  123. }
  124. const ExprCalc = {
  125. /**
  126. * 判断value是否是四则运算符
  127. * @param {String} value - 判断字符串
  128. * @return {Boolean}
  129. */
  130. isOperator(value) {
  131. const operatorString = "+-*/()";
  132. return operatorString.indexOf(value) > -1;
  133. },
  134. /**
  135. * Rpn解析栈
  136. * @param exp
  137. * @returns {Array}
  138. */
  139. parse2Rpn(exp) {
  140. const getPriority = function (value){
  141. switch(value){
  142. case '+':
  143. case '-':
  144. return 1;
  145. case '*':
  146. case '/':
  147. return 2;
  148. default:
  149. return 0;
  150. }
  151. };
  152. const priority = function (o1, o2){
  153. return getPriority(o1) <= getPriority(o2);
  154. };
  155. const inputStack = [];
  156. const outputStack = [];
  157. const outputQueue = [];
  158. for (let i = 0, len = exp.length; i < len; i++) {
  159. const cur = exp[i];
  160. if (cur !== ' ' ) {
  161. inputStack.push(cur);
  162. }
  163. }
  164. let num = '', isNumPre = false, isOperatorPre = false;
  165. while(inputStack.length > 0){
  166. const cur = inputStack.shift();
  167. if (this.isOperator(cur) && !(cur === '-' && !isNumPre)) {
  168. if (isNumPre) {
  169. outputQueue.push(parseFloat(num));
  170. num = '';
  171. }
  172. if (cur === '(') {
  173. outputStack.push(cur);
  174. } else if (cur === ')') {
  175. let po = outputStack.pop();
  176. while (po !== '(' && outputStack.length > 0) {
  177. outputQueue.push(po);
  178. po = outputStack.pop();
  179. }
  180. if (po !== '(') {
  181. throw "error: unmatched ()";
  182. }
  183. } else {
  184. while(priority(cur, outputStack[outputStack.length - 1]) && outputStack.length > 0){
  185. outputQueue.push(outputStack.pop());
  186. }
  187. outputStack.push(cur);
  188. }
  189. isNumPre = false;
  190. isOperatorPre = true;
  191. } else {
  192. num = num + cur;
  193. isNumPre = true;
  194. isOperatorPre = false;
  195. if (inputStack.length === 0) {
  196. outputQueue.push(parseFloat(num));
  197. }
  198. }
  199. }
  200. if (outputStack.length > 0) {
  201. if (outputStack[outputStack.length - 1] === ')' || outputStack[outputStack.length - 1] === '(') {
  202. throw "error: unmatched ()";
  203. }
  204. while (outputStack.length > 0) {
  205. outputQueue.push(outputStack.pop());
  206. }
  207. }
  208. return outputQueue;
  209. },
  210. /**
  211. * 计算Rpn解析栈
  212. * @param rpnQueue
  213. * @returns {*}
  214. */
  215. evalRpn(rpnQueue) {
  216. const getResult = function (num1, num2, opera) {
  217. switch (opera) {
  218. case '+':
  219. return add(num1, num2);
  220. case '-':
  221. return sub(num1, num2);
  222. case '*':
  223. return mul(num1, num2, 10);
  224. case '/':
  225. return div(num1, num2, 10);
  226. default:
  227. throw '参数错误';
  228. }
  229. };
  230. const outputStack = [];
  231. while(rpnQueue.length > 0){
  232. const cur = rpnQueue.shift();
  233. if (!this.isOperator(cur)) {
  234. outputStack.push(cur);
  235. } else {
  236. if (outputStack.length < 2) {
  237. throw "unvalid stack length";
  238. }
  239. const sec = outputStack.pop();
  240. const fir = outputStack.pop();
  241. outputStack.push(getResult(fir, sec, cur));
  242. }
  243. }
  244. if (outputStack.length !== 1) {
  245. throw "unvalid expression";
  246. } else {
  247. return outputStack[0];
  248. }
  249. },
  250. /**
  251. * 解析四则运算字符串并计算
  252. * @param {String} expr
  253. * @returns
  254. */
  255. calcExprStrRpn(expr) {
  256. try {
  257. if (!isNaN(Number(expr))) {
  258. return Number(expr);
  259. } else {
  260. const rpnArr = this.parse2Rpn(expr);
  261. const result = this.evalRpn(rpnArr);
  262. return result === Infinity ? null : result;
  263. }
  264. } catch (err) {
  265. return null;
  266. }
  267. },
  268. };
  269. function checkMathConfig () {
  270. if (!mathConfig) {
  271. math && math.config({
  272. number: 'BigNumber',
  273. });
  274. mathConfig = true;
  275. }
  276. }
  277. function mathCalcExpr(expr) {
  278. checkMathConfig();
  279. return parseFloat(math.evaluate(expr));
  280. }
  281. function tansExpr(expr) {
  282. let formula = $.trim(expr).replace('\t', '').replace('=', '');
  283. const percent = formula.match(percentReg);
  284. if (percent) {
  285. for (const p of percent) {
  286. const v = math.eval(p.replace(new RegExp('%', 'gm'), '/100'));
  287. formula = formula.replace(p, v);
  288. }
  289. }
  290. return formula;
  291. }
  292. return {add, sum, sub, mul, div, round, isNonZero: zhBaseCalc.isNonZero, calcExpr: ExprCalc, mathCalcExpr}
  293. })();