index.es.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /**
  2. * @desc 解决浮动运算问题,避免小数点后产生多位数和计算精度损失。
  3. *
  4. * 问题示例:2.3 + 2.4 = 4.699999999999999,1.0 - 0.9 = 0.09999999999999998
  5. */
  6. /**
  7. * Correct the given number to specifying significant digits.
  8. *
  9. * @param num The input number
  10. * @param precision An integer specifying the number of significant digits
  11. *
  12. * @example strip(0.09999999999999998) === 0.1 // true
  13. */
  14. function strip(num, precision) {
  15. if (precision === void 0) { precision = 15; }
  16. return +parseFloat(Number(num).toPrecision(precision));
  17. }
  18. /**
  19. * Return digits length of a number.
  20. *
  21. * @param num The input number
  22. */
  23. function digitLength(num) {
  24. // Get digit length of e
  25. var eSplit = num.toString().split(/[eE]/);
  26. var len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
  27. return len > 0 ? len : 0;
  28. }
  29. /**
  30. * Convert the given number to integer, support scientific notation.
  31. * The number will be scale up if it is decimal.
  32. *
  33. * @param num The input number
  34. */
  35. function float2Fixed(num) {
  36. if (num.toString().indexOf('e') === -1) {
  37. return Number(num.toString().replace('.', ''));
  38. }
  39. var dLen = digitLength(num);
  40. return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
  41. }
  42. /**
  43. * Log a warning if the given number is out of bounds.
  44. *
  45. * @param num The input number
  46. */
  47. function checkBoundary(num) {
  48. if (_boundaryCheckingState) {
  49. if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
  50. console.warn(num + " is beyond boundary when transfer to integer, the results may not be accurate");
  51. }
  52. }
  53. }
  54. /**
  55. * Create an operation to support rest params.
  56. *
  57. * @param operation The original operation
  58. */
  59. function createOperation(operation) {
  60. return function () {
  61. var nums = [];
  62. for (var _i = 0; _i < arguments.length; _i++) {
  63. nums[_i] = arguments[_i];
  64. }
  65. var first = nums[0], others = nums.slice(1);
  66. return others.reduce(function (prev, next) { return operation(prev, next); }, first);
  67. };
  68. }
  69. /**
  70. * Accurate multiplication.
  71. *
  72. * @param nums The numbers to multiply
  73. */
  74. var times = createOperation(function (num1, num2) {
  75. var num1Changed = float2Fixed(num1);
  76. var num2Changed = float2Fixed(num2);
  77. var baseNum = digitLength(num1) + digitLength(num2);
  78. var leftValue = num1Changed * num2Changed;
  79. checkBoundary(leftValue);
  80. return leftValue / Math.pow(10, baseNum);
  81. });
  82. /**
  83. * Accurate addition.
  84. *
  85. * @param nums The numbers to add
  86. */
  87. var plus = createOperation(function (num1, num2) {
  88. // 取最大的小数位
  89. var baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
  90. // 把小数都转为整数然后再计算
  91. return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
  92. });
  93. /**
  94. * Accurate subtraction.
  95. *
  96. * @param nums The numbers to subtract
  97. */
  98. var minus = createOperation(function (num1, num2) {
  99. var baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
  100. return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
  101. });
  102. /**
  103. * Accurate division.
  104. *
  105. * @param nums The numbers to divide
  106. */
  107. var divide = createOperation(function (num1, num2) {
  108. var num1Changed = float2Fixed(num1);
  109. var num2Changed = float2Fixed(num2);
  110. checkBoundary(num1Changed);
  111. checkBoundary(num2Changed);
  112. // fix: 类似 10 ** -4 为 0.00009999999999999999,strip 修正
  113. return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
  114. });
  115. /**
  116. * Accurate rounding method.
  117. *
  118. * @param num The number to round
  119. * @param decimal An integer specifying the decimal digits
  120. */
  121. function round(num, decimal) {
  122. var base = Math.pow(10, decimal);
  123. var result = divide(Math.round(Math.abs(times(num, base))), base);
  124. if (num < 0 && result !== 0) {
  125. result = times(result, -1);
  126. }
  127. return result;
  128. }
  129. var _boundaryCheckingState = true;
  130. /**
  131. * Whether to check the bounds of number, default is enabled.
  132. *
  133. * @param flag The value to indicate whether is enabled
  134. */
  135. function enableBoundaryChecking(flag) {
  136. if (flag === void 0) { flag = true; }
  137. _boundaryCheckingState = flag;
  138. }
  139. var index = {
  140. strip: strip,
  141. plus: plus,
  142. minus: minus,
  143. times: times,
  144. divide: divide,
  145. round: round,
  146. digitLength: digitLength,
  147. float2Fixed: float2Fixed,
  148. enableBoundaryChecking: enableBoundaryChecking,
  149. };
  150. export { strip, plus, minus, times, divide, round, digitLength, float2Fixed, enableBoundaryChecking };
  151. export default index;