math.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /**
  2. * 数学模块,主要针对浮点型数据,在运算时的精度问题:
  3. * 1| 两个小数相加 0.1 + 0.2 = 0.30000000000000004
  4. * 2| 两个小数相乘 2.12 * 3.14 = 6.6568000000000005;
  5. * 3| ...
  6. * 4| ...
  7. * 5| 数值长度大于13位时 显示科学计数法
  8. * @author lautin
  9. * @created 2019-11-19 17:15:04
  10. */
  11. /**
  12. * 检测小数点精度
  13. * @param {number} num 传入的数值
  14. */
  15. function countDecimals(num) {
  16. let precision;
  17. try {
  18. precision = num.toString().split(".")[1].length;
  19. } catch (e) {
  20. precision = 0;
  21. }
  22. return precision;
  23. }
  24. /**
  25. * js在以下情景会自动将数值转换为科学计数法:
  26. * 小数点前的数字个数大于等于22位;
  27. * 小数点前边是0,小数点后十分位(包含十分位)之后连续0的个数>=6时
  28. * @param {number} 科学计数法显示的数值 1.2e-8
  29. * @return {number} 返回实际的数值 1.00000002
  30. */
  31. function scientific2No(val) { //-7.50375e-8
  32. // 正则匹配科学计数法的数字
  33. if (/\d+\.?\d*e[+-]*\d+/i.test(val)) {
  34. let zero = '0',
  35. parts = String(val).toLowerCase().split('e'), // ['-7.50375', '-8']
  36. e = parts.pop(), // 存储指数 -8
  37. l = Math.abs(e), // 0的个数 8
  38. sign = e / l, // 判断正负 -
  39. // 将系数按照小数点拆分
  40. coeff_array = parts[0].split('.'); // [-7, 50375] 去除中间的.
  41. // 如果恰好为8位 那么第二个数默认为undefined 需要重置为''
  42. if (!coeff_array[1]) coeff_array[1] = '';
  43. if (sign === -1) { // 小数
  44. // debugger;
  45. // 正数或者负数
  46. if (coeff_array[0] < 0) {
  47. val = '-' + zero + '.' + zero.repeat(l - 1) + coeff_array[0].slice(1) + coeff_array[1];
  48. } else {
  49. val = zero + '.' + zero.repeat(l - 1) + coeff_array.join(''); //拼接字符串,如果是小数,拼接0和小数点
  50. }
  51. } else {
  52. let dec = coeff_array[1];
  53. // 如果是整数,将整数除第一位之外的非零数字计入位数,相应的减少0的个数
  54. if (dec) l = l - dec.length;
  55. // 拼接字符串,如果是整数,不需要拼接小数点
  56. val = coeff_array.join('') + new Array(l + 1).join(zero);
  57. }
  58. }
  59. try {
  60. return val.toString();
  61. } catch (e) {
  62. return '';
  63. }
  64. }
  65. /**
  66. * 截取小数点后n位
  67. * @param {number} val 截取的数值
  68. * @param {number} scale 保留的小数点位数
  69. */
  70. function omitTo(val, scale) {
  71. // 转化科学计数法
  72. val = scientific2No(val);
  73. let ret;
  74. // 检测浮点数
  75. if (val.toString().indexOf(".") > -1) {
  76. // 提取实体集和精度值
  77. let [entity, precisionVal] = val.toString().split(".");
  78. if (precisionVal.length > scale) {
  79. // trunc() 方法会将数字的小数部分去掉,只保留整数部分。
  80. let tmp = scientific2No(Math.trunc(val * Math.pow(10, scale)));
  81. // 处理零值
  82. if (tmp == 0) ret = scientific2No('0.' + '0'.repeat(scale));
  83. else {
  84. // ret = tmp / ;
  85. ret = division(tmp, Math.pow(10, scale));
  86. try { // 小数
  87. let [a, b] = ret.toString().split(".");
  88. a = scientific2No(a),
  89. b = scientific2No(b);
  90. if (b.length < scale) {
  91. ret = a + '.' + b.padEnd(scale, '0');
  92. }
  93. } catch (e) { // 整数
  94. ret = ret + '.' + '0'.padEnd(scale, '0');
  95. }
  96. }
  97. } else if (precisionVal.length == scale) { // 精度
  98. ret = val;
  99. } else {
  100. // 补全小数点
  101. ret = entity + '.' + precisionVal.padEnd(scale, '0');
  102. }
  103. // 检测整型值
  104. } else ret = val + '.' + '0'.repeat(scale);
  105. // 去除末尾可能产生的多余的.
  106. if (ret.toString().endsWith('.')) ret = ret.slice(0, -1);
  107. return ret;
  108. }
  109. /**
  110. * 计算两个数的和
  111. * @param {number} num1
  112. * @param {number} num2
  113. */
  114. function add(num1, num2, scale = null) {
  115. num1 = scientific2No(num1);
  116. num2 = scientific2No(num2);
  117. let amplification, // 放大率
  118. precision1 = countDecimals(num1), // 精度1
  119. precision2 = countDecimals(num2); // 精度2
  120. amplification = Math.pow(10, Math.max(precision1, precision2));
  121. // 先放大再相加,然后除以放大率
  122. let val = (num1 * amplification + num2 * amplification) / amplification;
  123. // 转化科学计数法
  124. let result = scientific2No(val);
  125. // 控制显示长度
  126. if (scale) result = omitTo(result, scale);
  127. return result;
  128. }
  129. /**
  130. * 计算两个数的差值
  131. * @param {number} num1
  132. * @param {number} num2
  133. */
  134. function subtr(num1, num2, scale = null) {
  135. num1 = scientific2No(num1);
  136. num2 = scientific2No(num2);
  137. let amplification, // 放大率
  138. precision1 = countDecimals(num1), // 精度1
  139. precision2 = countDecimals(num2); // 精度2
  140. let precision = Math.max(precision1, precision2)
  141. amplification = Math.pow(10, precision);
  142. // 动态控制精度长度
  143. let val = ((num1 * amplification - num2 * amplification) / amplification).toFixed(precision);
  144. // 转化科学计数法
  145. let result = scientific2No(val);
  146. // 控制显示长度
  147. if (scale) result = omitTo(result, scale);
  148. return result;
  149. }
  150. /**
  151. * 计算两个数的乘积
  152. * @param {number} num1
  153. * @param {number} num2
  154. */
  155. function multiple(num1, num2, scale = null) {
  156. num1 = scientific2No(num1);
  157. num2 = scientific2No(num2);
  158. let precision = 0;
  159. precision += countDecimals(num1);
  160. precision += countDecimals(num2);
  161. let val = Number(num1.toString().replace(".", "")) * Number(num2.toString().replace(".", "")) / Math.pow(10, precision);
  162. let result = scientific2No(val);
  163. if (scale) result = omitTo(result, scale);
  164. return result;
  165. }
  166. /**
  167. * 两个数相除
  168. * @param {number} num1
  169. * @param {number} num2
  170. * @param {optional} d
  171. */
  172. function division(num1, num2, scale = null) {
  173. num1 = scientific2No(num1);
  174. num2 = scientific2No(num2);
  175. let precision1 = countDecimals(num1),
  176. precision2 = countDecimals(num2),
  177. m = precision1 > precision2 ? precision1 : precision2;
  178. // 两个整数相除 无需计算精度
  179. if (m <= 1) m = 1;
  180. let val = multiple(num1, m) / multiple(num2, m);
  181. let result = scientific2No(val);
  182. if (scale) result = omitTo(result, scale);
  183. return result;
  184. }
  185. Object.assign(Math, {
  186. countDecimals,
  187. add,
  188. subtr,
  189. multiple,
  190. division,
  191. scientific2No,
  192. omitTo
  193. })
  194. export default {
  195. countDecimals,
  196. add,
  197. subtr,
  198. multiple,
  199. division,
  200. scientific2No,
  201. omitTo
  202. }