index.js 4.9 KB

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