You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
3.8 KiB

9 months ago
  1. let _boundaryCheckingState = true; // 是否进行越界检查的全局开关
  2. /**
  3. * 把错误的数据转正
  4. * @private
  5. * @example strip(0.09999999999999998)=0.1
  6. */
  7. function strip(num, precision = 15) {
  8. return +parseFloat(Number(num).toPrecision(precision));
  9. }
  10. /**
  11. * Return digits length of a number
  12. * @private
  13. * @param {*number} num Input number
  14. */
  15. function digitLength(num) {
  16. // Get digit length of e
  17. const eSplit = num.toString().split(/[eE]/);
  18. const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
  19. return len > 0 ? len : 0;
  20. }
  21. /**
  22. * 把小数转成整数,如果是小数则放大成整数
  23. * @private
  24. * @param {*number} num 输入数
  25. */
  26. function float2Fixed(num) {
  27. if (num.toString().indexOf('e') === -1) {
  28. return Number(num.toString().replace('.', ''));
  29. }
  30. const dLen = digitLength(num);
  31. return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
  32. }
  33. /**
  34. * 检测数字是否越界如果越界给出提示
  35. * @private
  36. * @param {*number} num 输入数
  37. */
  38. function checkBoundary(num) {
  39. if (_boundaryCheckingState) {
  40. if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
  41. console.warn(`${num} 超出了精度限制,结果可能不正确`);
  42. }
  43. }
  44. }
  45. /**
  46. * 把递归操作扁平迭代化
  47. * @param {number[]} arr 要操作的数字数组
  48. * @param {function} operation 迭代操作
  49. * @private
  50. */
  51. function iteratorOperation(arr, operation) {
  52. const [num1, num2, ...others] = arr;
  53. let res = operation(num1, num2);
  54. others.forEach((num) => {
  55. res = operation(res, num);
  56. });
  57. return res;
  58. }
  59. /**
  60. * 高精度乘法
  61. * @export
  62. */
  63. export function times(...nums) {
  64. if (nums.length > 2) {
  65. return iteratorOperation(nums, times);
  66. }
  67. const [num1, num2] = nums;
  68. const num1Changed = float2Fixed(num1);
  69. const num2Changed = float2Fixed(num2);
  70. const baseNum = digitLength(num1) + digitLength(num2);
  71. const leftValue = num1Changed * num2Changed;
  72. checkBoundary(leftValue);
  73. return leftValue / Math.pow(10, baseNum);
  74. }
  75. /**
  76. * 高精度加法
  77. * @export
  78. */
  79. export function plus(...nums) {
  80. if (nums.length > 2) {
  81. return iteratorOperation(nums, plus);
  82. }
  83. const [num1, num2] = nums;
  84. // 取最大的小数位
  85. const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
  86. // 把小数都转为整数然后再计算
  87. return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
  88. }
  89. /**
  90. * 高精度减法
  91. * @export
  92. */
  93. export function minus(...nums) {
  94. if (nums.length > 2) {
  95. return iteratorOperation(nums, minus);
  96. }
  97. const [num1, num2] = nums;
  98. const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
  99. return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
  100. }
  101. /**
  102. * 高精度除法
  103. * @export
  104. */
  105. export function divide(...nums) {
  106. if (nums.length > 2) {
  107. return iteratorOperation(nums, divide);
  108. }
  109. const [num1, num2] = nums;
  110. const num1Changed = float2Fixed(num1);
  111. const num2Changed = float2Fixed(num2);
  112. checkBoundary(num1Changed);
  113. checkBoundary(num2Changed);
  114. // 重要,这里必须用strip进行修正
  115. return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
  116. }
  117. /**
  118. * 四舍五入
  119. * @export
  120. */
  121. export function round(num, ratio) {
  122. const base = Math.pow(10, ratio);
  123. let result = divide(Math.round(Math.abs(times(num, base))), base);
  124. if (num < 0 && result !== 0) {
  125. result = times(result, -1);
  126. }
  127. // 位数不足则补0
  128. return result;
  129. }
  130. /**
  131. * 是否进行边界检查默认开启
  132. * @param flag 标记开关true 为开启false 为关闭默认为 true
  133. * @export
  134. */
  135. export function enableBoundaryChecking(flag = true) {
  136. _boundaryCheckingState = flag;
  137. }
  138. export default {
  139. times,
  140. plus,
  141. minus,
  142. divide,
  143. round,
  144. enableBoundaryChecking,
  145. };