@mi-avalon/libs 0.0.25 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,92 +1,366 @@
1
1
  class CMX {
2
+ static factorialCache = new Map();
2
3
  /**
3
- **
4
- **/
5
- add = function (arg1, arg2) {
6
- let r1, r2, m, c;
7
- try {
8
- r1 = arg1.toString().split('.')[1].length;
9
- }
10
- catch (e) {
11
- r1 = 0;
12
- }
13
- try {
14
- r2 = arg2.toString().split('.')[1].length;
15
- }
16
- catch (e) {
17
- r2 = 0;
18
- }
19
- c = Math.abs(r1 - r2);
20
- m = Math.pow(10, Math.max(r1, r2));
21
- if (c > 0) {
22
- let cm = Math.pow(10, c);
23
- if (r1 > r2) {
24
- arg1 = Number(arg1.toString().replace('.', ''));
25
- arg2 = Number(arg2.toString().replace('.', '')) * cm;
26
- }
27
- else {
28
- arg1 = Number(arg1.toString().replace('.', '')) * cm;
29
- arg2 = Number(arg2.toString().replace('.', ''));
4
+ * 获取数字的小数位数长度
5
+ * @private
6
+ * @param num 要检查的数字
7
+ * @returns 小数位数长度
8
+ */
9
+ static getDecimalLength(num) {
10
+ const str = num.toString();
11
+ const decimalIndex = str.indexOf('.');
12
+ return decimalIndex === -1 ? 0 : str.length - decimalIndex - 1;
13
+ }
14
+ /**
15
+ * 将数字转换为整数(移除小数点)
16
+ * @private
17
+ * @param num 要转换的数字
18
+ * @param decimalPlaces 需要补零的小数位数
19
+ * @returns 转换后的整数值
20
+ */
21
+ static toInteger(num, decimalPlaces) {
22
+ const str = num.toString().replace('.', '');
23
+ return Number(str) * Math.pow(10, decimalPlaces);
24
+ }
25
+ /**
26
+ * 精确加法运算(支持多个参数)
27
+ * @param args 要相加的数字序列
28
+ * @returns 所有数字的和
29
+ * @example
30
+ * cmx.add(0.1, 0.2) // 返回 0.3
31
+ * cmx.add(1, 2, 3) // 返回 6
32
+ */
33
+ add(...args) {
34
+ if (args.length < 2)
35
+ return args[0] || 0;
36
+ return args.reduce((acc, current) => {
37
+ // 获取两个数的小数位数
38
+ const r1 = CMX.getDecimalLength(acc);
39
+ const r2 = CMX.getDecimalLength(current);
40
+ // 计算最大小数位数和对应的10的幂
41
+ const maxDecimal = Math.max(r1, r2);
42
+ const m = Math.pow(10, maxDecimal);
43
+ // 将两个数调整为相同小数位数的整数
44
+ const adjusted1 = CMX.toInteger(acc, maxDecimal - r1);
45
+ const adjusted2 = CMX.toInteger(current, maxDecimal - r2);
46
+ // 相加后还原小数位
47
+ return (adjusted1 + adjusted2) / m;
48
+ });
49
+ }
50
+ /**
51
+ * 精确减法运算
52
+ * @param arg1 被减数
53
+ * @param arg2 减数
54
+ * @returns 两数之差
55
+ * @example
56
+ * cmx.sub(0.3, 0.1) // 返回 0.2
57
+ */
58
+ sub(arg1, arg2) {
59
+ const r1 = CMX.getDecimalLength(arg1);
60
+ const r2 = CMX.getDecimalLength(arg2);
61
+ const maxDecimal = Math.max(r1, r2);
62
+ const m = Math.pow(10, maxDecimal);
63
+ // 先转换为整数运算,再还原小数位
64
+ return Number(((arg1 * m - arg2 * m) / m).toFixed(maxDecimal));
65
+ }
66
+ /**
67
+ * 精确乘法运算(支持多个参数)
68
+ * @param args 要相乘的数字序列
69
+ * @returns 所有数字的乘积
70
+ * @example
71
+ * cmx.mul(0.1, 0.2) // 返回 0.02
72
+ * cmx.mul(2, 3, 4) // 返回 24
73
+ */
74
+ mul(...args) {
75
+ if (args.length < 2)
76
+ return args[0] || 0;
77
+ return args.reduce((acc, current) => {
78
+ // 计算总小数位数
79
+ const decimalPlaces = CMX.getDecimalLength(acc) + CMX.getDecimalLength(current);
80
+ // 移除小数点转换为整数
81
+ const adjusted1 = Number(acc.toString().replace('.', ''));
82
+ const adjusted2 = Number(current.toString().replace('.', ''));
83
+ // 相乘后还原总小数位
84
+ return (adjusted1 * adjusted2) / Math.pow(10, decimalPlaces);
85
+ });
86
+ }
87
+ /**
88
+ * 精确除法运算
89
+ * @param arg1 被除数
90
+ * @param arg2 除数
91
+ * @returns 两数之商
92
+ * @throws {Error} 除数为零时抛出错误
93
+ * @example
94
+ * cmx.div(0.3, 0.1) // 返回 3
95
+ */
96
+ div(arg1, arg2) {
97
+ if (arg2 === 0)
98
+ throw new Error('除数不能为零');
99
+ // 添加误差补偿
100
+ const compensation = 1 + Number.EPSILON;
101
+ const t1 = CMX.getDecimalLength(arg1);
102
+ const t2 = CMX.getDecimalLength(arg2);
103
+ const r1 = Number(arg1.toString().replace('.', '')) * compensation;
104
+ const r2 = Number(arg2.toString().replace('.', '')) * compensation;
105
+ return (r1 / r2) * Math.pow(10, t2 - t1);
106
+ }
107
+ /**
108
+ * 精确四舍五入
109
+ * @param num 要舍入的数字
110
+ * @param decimalPlaces 保留的小数位数(默认为0)
111
+ * @returns 舍入后的数字
112
+ * @example
113
+ * cmx.round(1.2345, 2) // 返回 1.23
114
+ */
115
+ round(num, decimalPlaces = 0) {
116
+ const m = Math.pow(10, decimalPlaces);
117
+ return Math.round(num * m) / m;
118
+ }
119
+ /**
120
+ * 计算百分比值
121
+ * @param value 基础值
122
+ * @param percent 百分比
123
+ * @returns 计算后的百分比值
124
+ * @example
125
+ * cmx.percent(200, 15) // 返回 30(200的15%)
126
+ */
127
+ percent(value, percent) {
128
+ return this.div(this.mul(value, percent), 100);
129
+ }
130
+ /**
131
+ * 计算平均数
132
+ * @param args 要计算平均数的数字序列
133
+ * @returns 平均数
134
+ * @example
135
+ * cmx.average(1, 2, 3) // 返回 2
136
+ */
137
+ average(...args) {
138
+ if (args.length === 0)
139
+ return 0;
140
+ return this.div(args.reduce((a, b) => this.add(a, b)), args.length);
141
+ }
142
+ /**
143
+ * 判断两个数是否近似相等(解决浮点数精度问题)
144
+ * @param a 第一个数
145
+ * @param b 第二个数
146
+ * @param tolerance 允许的误差范围(默认为1e-10)
147
+ * @returns 如果差值小于误差范围则返回true
148
+ * @example
149
+ * cmx.approxEqual(0.1 + 0.2, 0.3) // 返回 true
150
+ */
151
+ approxEqual(a, b, tolerance = 1e-10) {
152
+ return Math.abs(this.sub(a, b)) < tolerance;
153
+ }
154
+ /**
155
+ * 计算自然对数(ln(x))
156
+ * @param num 要计算对数的数字
157
+ * @param decimalPlaces 结果保留的小数位数(默认保留10位)
158
+ * @returns 自然对数值
159
+ * @throws {Error} 当输入小于等于0时抛出错误
160
+ * @example
161
+ * cmx.ln(1) // 返回 0
162
+ * cmx.ln(Math.E) // 返回 1
163
+ */
164
+ ln(num, decimalPlaces = 10) {
165
+ if (num <= 0)
166
+ throw new Error('对数函数的输入必须为正数');
167
+ if (num === 1)
168
+ return 0;
169
+ // 使用泰勒级数展开计算自然对数
170
+ let result = 0;
171
+ const tolerance = Math.pow(10, -decimalPlaces - 1);
172
+ let term = this.div(this.sub(num, 1), this.add(num, 1));
173
+ const termSquared = this.mul(term, term);
174
+ let currentTerm = term;
175
+ let n = 1;
176
+ do {
177
+ result = this.add(result, this.div(currentTerm, n));
178
+ currentTerm = this.mul(currentTerm, termSquared);
179
+ n += 2;
180
+ } while (Math.abs(currentTerm) > tolerance);
181
+ return this.round(this.mul(2, result), decimalPlaces);
182
+ }
183
+ /**
184
+ * 高精度指数函数(解决Math.exp的精度问题)
185
+ * @param x 指数
186
+ * @param decimalPlaces 精度位数(默认10位)
187
+ * @returns e^x 的精确值
188
+ * @example
189
+ * cmx.exp(1) // 2.7182818285(比Math.exp(1)更可控精度)
190
+ * cmx.exp(-3.5) // 0.0301973834
191
+ */
192
+ exp(x, decimalPlaces = 10) {
193
+ // 添加提前终止条件
194
+ if (x === 0)
195
+ return 1;
196
+ if (x === 1)
197
+ return Math.E;
198
+ let result = 1;
199
+ let term = 1;
200
+ const tolerance = Math.pow(10, -decimalPlaces - 1);
201
+ let n = 1;
202
+ let prevResult = 0;
203
+ do {
204
+ prevResult = result;
205
+ term = this.div(this.mul(term, x), n);
206
+ result = this.add(result, term);
207
+ n++;
208
+ } while (Math.abs(term) > tolerance &&
209
+ !this.approxEqual(result, prevResult, tolerance) // 新增终止条件
210
+ );
211
+ return this.round(result, decimalPlaces);
212
+ }
213
+ /**
214
+ * 精确计算平方根(解决Math.sqrt的精度问题)
215
+ * @param num 输入数字(必须 ≥0)
216
+ * @param decimalPlaces 结果精度(默认10位)
217
+ * @returns 精确的平方根值
218
+ * @throws {Error} 输入负数时抛出
219
+ * @example
220
+ * cmx.sqrt(2) // 1.4142135624 (Math.sqrt(2)可能产生微小误差)
221
+ */
222
+ sqrt(num, decimalPlaces = 10) {
223
+ if (num < 0)
224
+ throw new Error('负数平方根需使用复数计算');
225
+ if (num === 0)
226
+ return 0;
227
+ // 牛顿迭代法+自定义精度控制
228
+ let guess = num / 2;
229
+ const tolerance = Math.pow(10, -decimalPlaces - 1);
230
+ do {
231
+ guess = this.div(this.add(guess, this.div(num, guess)), 2);
232
+ } while (Math.abs(this.sub(this.mul(guess, guess), num)) > tolerance);
233
+ return this.round(guess, decimalPlaces);
234
+ }
235
+ /**
236
+ * 安全对数计算(解决Math.log的边界问题)
237
+ * @param num 输入数字(支持 ≤0 的特殊处理)
238
+ * @param options 配置项:
239
+ * - safe: 是否返回安全值(默认true)
240
+ * - replaceNeg: 负数替换值(默认NaN)
241
+ * - replaceZero: 零替换值(默认-Infinity)
242
+ * @returns 处理后的结果
243
+ * @example
244
+ * cmx.safeLog(-1) // NaN(Math.log直接报错)
245
+ * cmx.safeLog(0) // -Infinity(可配置替换值)
246
+ */
247
+ safeLog(num, options = {}) {
248
+ const { safe = true, replaceNeg = NaN, replaceZero = -Infinity } = options;
249
+ if (num > 0)
250
+ return Math.log(num);
251
+ if (!safe)
252
+ throw new Error('Invalid input');
253
+ return num === 0 ? replaceZero : replaceNeg;
254
+ }
255
+ /**
256
+ * 精确阶乘(解决Math无阶乘方法的问题)
257
+ * @param n 输入整数(0≤n≤170)
258
+ * @returns 精确结果(超过170返回Infinity)
259
+ * @example
260
+ * cmx.factorial(20) // 2432902008176640000(JS整数精确范围)
261
+ */
262
+ factorial(n) {
263
+ if (!Number.isInteger(n) || n < 0)
264
+ return NaN;
265
+ if (n < 0)
266
+ return NaN;
267
+ if (n > Number.MAX_SAFE_INTEGER) {
268
+ // 使用BigInt计算后转换为科学计数法
269
+ let result = BigInt(1);
270
+ for (let i = 2; i <= n; i++) {
271
+ result *= BigInt(i);
30
272
  }
273
+ return Number(result.toString());
31
274
  }
32
- else {
33
- arg1 = Number(arg1?.toString().replace('.', ''));
34
- arg2 = Number(arg2?.toString().replace('.', ''));
35
- }
36
- return (arg1 + arg2) / m;
37
- };
38
- /**
39
- **
40
- **/
41
- sub = function (arg1, arg2) {
42
- let r1, r2, m, n;
43
- try {
44
- r1 = arg1.toString().split('.')[1].length;
45
- }
46
- catch (e) {
47
- r1 = 0;
48
- }
49
- try {
50
- r2 = arg2.toString().split('.')[1].length;
51
- }
52
- catch (e) {
53
- r2 = 0;
275
+ if (n > 170)
276
+ return Infinity;
277
+ if (CMX.factorialCache.has(n))
278
+ return CMX.factorialCache.get(n);
279
+ let result = 1;
280
+ for (let i = 2; i <= n; i++) {
281
+ if (CMX.factorialCache.has(i)) {
282
+ result = CMX.factorialCache.get(i);
283
+ continue;
284
+ }
285
+ result = this.mul(result, i);
286
+ CMX.factorialCache.set(i, result); // 缓存中间结果
54
287
  }
55
- m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //动态控制精度长度
56
- n = r1 >= r2 ? r1 : r2;
57
- return Number(((arg1 * m - arg2 * m) / m).toFixed(n));
58
- };
59
- /**
60
- **
61
- **/
62
- mul = function (arg1, arg2) {
63
- let m = 0, s1 = arg1.toString(), s2 = arg2.toString();
64
- try {
65
- m += s1.split('.')[1].length;
288
+ return result;
289
+ }
290
+ /**
291
+ * 双曲函数精确计算(解决大数精度问题)
292
+ * @param x 输入值
293
+ * @param type 函数类型:'sinh' | 'cosh' | 'tanh'
294
+ * @param decimalPlaces 精度(默认10位)
295
+ * @returns 计算结果
296
+ * @example
297
+ * cmx.hypFunc(1, 'tanh') // 0.761594156(比Math.tanh更精确)
298
+ */
299
+ hypFunc(x, type, decimalPlaces = 10) {
300
+ // 使用泰勒展开式避免大数计算误差
301
+ const expX = this.exp(x, decimalPlaces + 2);
302
+ const expNegX = this.exp(-x, decimalPlaces + 2);
303
+ switch (type) {
304
+ case 'sinh':
305
+ return this.round(this.div(this.sub(expX, expNegX), 2), decimalPlaces);
306
+ case 'cosh':
307
+ return this.round(this.div(this.add(expX, expNegX), 2), decimalPlaces);
308
+ case 'tanh':
309
+ return this.round(this.div(this.sub(expX, expNegX), this.add(expX, expNegX)), decimalPlaces);
310
+ default:
311
+ throw new Error('不支持的函数类型');
66
312
  }
67
- catch (e) { }
68
- try {
69
- m += s2.split('.')[1].length;
313
+ }
314
+ /**
315
+ * 精确排列组合(解决大数计算问题)
316
+ * @param n 总数
317
+ * @param k 选取数
318
+ * @param type 计算类型:'permutation' | 'combination'
319
+ * @returns 精确结果
320
+ * @example
321
+ * cmx.permComb(100, 5, 'combination') // 75287520
322
+ */
323
+ permComb(n, k, type) {
324
+ if (!Number.isInteger(n) || !Number.isInteger(k) || k > n || k < 0) {
325
+ return NaN;
70
326
  }
71
- catch (e) { }
72
- return ((Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m));
73
- };
74
- /**
75
- **
76
- **/
77
- div = function (arg1, arg2) {
78
- let t1 = 0, t2 = 0, r1, r2;
79
- try {
80
- t1 = arg1.toString().split('.')[1].length;
327
+ let result = 1;
328
+ for (let i = 0; i < k; i++) {
329
+ result = this.mul(result, n - i);
330
+ if (type === 'combination') {
331
+ result = this.div(result, i + 1);
332
+ }
81
333
  }
82
- catch (e) { }
83
- try {
84
- t2 = arg2.toString().split('.')[1].length;
334
+ return result;
335
+ }
336
+ /**
337
+ * 计算最大公约数(GCD)
338
+ * @param a 第一个数字
339
+ * @param b 第二个数字
340
+ * @returns 最大公约数
341
+ * @example
342
+ * cmx.gcd(48, 18) // 返回 6
343
+ */
344
+ gcd(a, b) {
345
+ a = Math.abs(a);
346
+ b = Math.abs(b);
347
+ while (b !== 0) {
348
+ const temp = b;
349
+ b = a % b;
350
+ a = temp;
85
351
  }
86
- catch (e) { }
87
- r1 = Number(arg1.toString().replace('.', ''));
88
- r2 = Number(arg2.toString().replace('.', ''));
89
- return (r1 / r2) * Math.pow(10, t2 - t1);
90
- };
352
+ return a;
353
+ }
354
+ /**
355
+ * 计算最小公倍数(LCM)
356
+ * @param a 第一个数字
357
+ * @param b 第二个数字
358
+ * @returns 最小公倍数
359
+ * @example
360
+ * cmx.lcm(12, 15) // 返回 60
361
+ */
362
+ lcm(a, b) {
363
+ return this.div(this.mul(a, b), this.gcd(a, b));
364
+ }
91
365
  }
92
366
  export const cmx = new CMX();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mi-avalon/libs",
3
3
  "private": false,
4
- "version": "0.0.25",
4
+ "version": "0.0.27",
5
5
  "type": "module",
6
6
  "main": "./dist/index.umd.js",
7
7
  "module": "./dist/index.es.js",
@@ -16,7 +16,7 @@
16
16
  "dev": "vite",
17
17
  "build": "vite build && tsc",
18
18
  "preview": "vite preview",
19
- "pub": "npm run build && npm publish"
19
+ "pub": "pnpm build && npm run publish"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^20.0.0",
@@ -34,5 +34,8 @@
34
34
  "antd": "^4.0.0 || ^5.0.0",
35
35
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
36
36
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
37
+ },
38
+ "dependencies": {
39
+ "@ant-design/icons": "^6.0.1"
37
40
  }
38
41
  }