@tendaui/components 1.3.0 → 1.3.1
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.
- package/LICENSE +21 -21
- package/README.md +176 -176
- package/button/_example/ghost.tsx +2 -5
- package/color-picker/utils/color-picker/cmyk.ts +89 -89
- package/color-picker/utils/color-picker/color.ts +467 -467
- package/color-picker/utils/color-picker/constants.ts +187 -187
- package/color-picker/utils/color-picker/draggable.ts +99 -99
- package/color-picker/utils/color-picker/format.ts +90 -90
- package/color-picker/utils/color-picker/gradient.ts +237 -237
- package/color-picker/utils/color-picker/index.ts +7 -7
- package/color-picker/utils/color-picker/types.ts +33 -33
- package/dialog/hooks/useDialogPosition.ts +35 -35
- package/form/type.ts +519 -519
- package/global-config/default-config.ts +95 -95
- package/global-config/locale/ar_KW.ts +259 -259
- package/global-config/locale/en_US.ts +265 -265
- package/global-config/locale/it_IT.ts +264 -264
- package/global-config/locale/ja_JP.ts +264 -264
- package/global-config/locale/ko_KR.ts +264 -264
- package/global-config/locale/ru_RU.ts +277 -277
- package/global-config/locale/zh_CN.ts +265 -265
- package/global-config/locale/zh_TW.ts +265 -265
- package/global-config/mobile/default-config.ts +6 -6
- package/global-config/mobile/locale/ar_KW.ts +112 -112
- package/global-config/mobile/locale/en_US.ts +113 -113
- package/global-config/mobile/locale/it_IT.ts +113 -113
- package/global-config/mobile/locale/ja_JP.ts +100 -100
- package/global-config/mobile/locale/ko_KR.ts +100 -100
- package/global-config/mobile/locale/ru_RU.ts +112 -112
- package/global-config/mobile/locale/zh_CN.ts +100 -100
- package/global-config/mobile/locale/zh_TW.ts +100 -100
- package/global-config/t.ts +111 -111
- package/input-number/InputNumber.tsx +124 -124
- package/input-number/defaultProps.ts +17 -17
- package/input-number/index.ts +9 -9
- package/input-number/style/css.js +1 -1
- package/input-number/style/index.js +1 -1
- package/input-number/useInputNumber.tsx +270 -270
- package/list/ListItem.tsx +36 -36
- package/list/ListItemMeta.tsx +40 -40
- package/list/defaultProps.ts +11 -11
- package/list/hooks/useListVirtualScroll.ts +82 -82
- package/list/style/css.js +1 -1
- package/list/style/index.js +1 -1
- package/locale/LocalReceiver.ts +55 -55
- package/locale/ar_KW.ts +7 -7
- package/locale/en_US.ts +7 -7
- package/locale/it_IT.ts +6 -6
- package/locale/ja_JP.ts +6 -6
- package/locale/ko_KR.ts +6 -6
- package/locale/ru_RU.ts +6 -6
- package/locale/zh_CN.ts +5 -5
- package/locale/zh_TW.ts +7 -7
- package/package.json +2 -2
- package/radio/_example/default.tsx +2 -2
- package/select/type.ts +382 -382
- package/select-input/type.ts +280 -280
- package/slider/SliderHandleButton.tsx +50 -50
- package/slider/defaultProps.ts +15 -15
- package/slider/style/css.js +1 -1
- package/slider/style/index.js +1 -1
- package/styles/_global.scss +40 -40
- package/styles/_vars.scss +374 -374
- package/styles/components/alert/_index.scss +175 -175
- package/styles/components/alert/_vars.scss +41 -41
- package/styles/components/badge/_index.scss +71 -71
- package/styles/components/badge/_vars.scss +26 -26
- package/styles/components/button/_index.scss +499 -499
- package/styles/components/button/_mixins.scss +40 -40
- package/styles/components/button/_vars.scss +121 -121
- package/styles/components/checkbox/_index.scss +158 -158
- package/styles/components/checkbox/_var.scss +59 -59
- package/styles/components/color-picker/_index.scss +586 -586
- package/styles/components/color-picker/_vars.scss +79 -79
- package/styles/components/dialog/_animate.scss +133 -133
- package/styles/components/dialog/_index.scss +310 -310
- package/styles/components/dialog/_vars.scss +60 -60
- package/styles/components/drawer/_index.scss +206 -206
- package/styles/components/drawer/_var.scss +55 -55
- package/styles/components/fireworks/_index.scss +86 -86
- package/styles/components/fireworks/_vars.scss +5 -5
- package/styles/components/form/_index.scss +175 -175
- package/styles/components/form/_mixins.scss +74 -74
- package/styles/components/form/_vars.scss +101 -101
- package/styles/components/input/_index.scss +350 -350
- package/styles/components/input/_mixins.scss +120 -120
- package/styles/components/input/_vars.scss +130 -130
- package/styles/components/input-number/_index.scss +327 -327
- package/styles/components/input-number/_vars.scss +56 -56
- package/styles/components/ip-input/_index.scss +277 -277
- package/styles/components/layout/_index.scss +47 -47
- package/styles/components/layout/_vars.scss +19 -19
- package/styles/components/layout/doc.scss +74 -74
- package/styles/components/list/_index.scss +172 -172
- package/styles/components/list/_vars.scss +42 -42
- package/styles/components/loading/_index.scss +113 -113
- package/styles/components/loading/_vars.scss +40 -40
- package/styles/components/notification/_index.scss +140 -140
- package/styles/components/notification/_mixins.scss +13 -13
- package/styles/components/notification/_vars.scss +60 -60
- package/styles/components/popup/_index.scss +78 -78
- package/styles/components/popup/_mixin.scss +149 -149
- package/styles/components/popup/_vars.scss +33 -33
- package/styles/components/radio/_index.scss +376 -376
- package/styles/components/radio/_vars.scss +89 -89
- package/styles/components/select/_index.scss +291 -291
- package/styles/components/select/_var.scss +64 -64
- package/styles/components/select-input/_index.scss +5 -5
- package/styles/components/select-input/_var.scss +4 -4
- package/styles/components/slider/_index.scss +241 -241
- package/styles/components/slider/_vars.scss +51 -51
- package/styles/components/switch/_index.scss +175 -175
- package/styles/components/switch/_vars.scss +63 -63
- package/styles/components/table/_index.scss +194 -194
- package/styles/components/table/_var.scss +52 -52
- package/styles/components/tabs/_index.scss +165 -165
- package/styles/components/tabs/_mixins.scss +11 -11
- package/styles/components/tabs/_vars.scss +72 -72
- package/styles/components/tag/_index.scss +317 -317
- package/styles/components/tag/_var.scss +86 -86
- package/styles/components/tag-input/_index.scss +164 -164
- package/styles/components/tag-input/_vars.scss +17 -17
- package/styles/mixins/_focus.scss +8 -8
- package/styles/mixins/_layout.scss +32 -32
- package/styles/mixins/_reset.scss +11 -11
- package/styles/mixins/_scrollbar.scss +32 -32
- package/styles/mixins/_text.scss +50 -50
- package/styles/themes/_dark.scss +169 -169
- package/styles/themes/_font.scss +69 -69
- package/styles/themes/_index.scss +5 -5
- package/styles/themes/_light.scss +170 -170
- package/styles/themes/_radius.scss +9 -9
- package/styles/themes/_size.scss +68 -68
- package/styles/utilities/_animation.scss +58 -58
- package/styles/utilities/_tips.scss +10 -10
- package/switch/_example/with-label.tsx +1 -1
- package/utils/input-number/large-number.ts +423 -423
- package/utils/input-number/number.ts +257 -257
- package/utils/log/index.ts +3 -3
- package/utils/log/log.ts +29 -29
- package/utils/log/types.ts +9 -9
- package/utils/style.ts +58 -58
|
@@ -1,423 +1,423 @@
|
|
|
1
|
-
import { isString, isNumber, isObject } from "lodash-es";
|
|
2
|
-
import log from "../log/log";
|
|
3
|
-
|
|
4
|
-
export type InputNumberDecimalPlaces = number | { enableRound: boolean; places: number };
|
|
5
|
-
|
|
6
|
-
export function fillZero(length: number) {
|
|
7
|
-
return new Array(length).fill(0).join("");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 大数,是否是一个数字,数字字符包括 - . e [0-9]
|
|
12
|
-
*/
|
|
13
|
-
export function isInputNumber(num: number | string): boolean {
|
|
14
|
-
if (!num) return true;
|
|
15
|
-
if (isNumber(num)) return !Number.isNaN(num);
|
|
16
|
-
const r = /^[0-9|e|E|-]+\.*[0-9|e|E|-]*$/.test(num);
|
|
17
|
-
if (!r) return false;
|
|
18
|
-
// only allow one [.e] and two [-]
|
|
19
|
-
let eCount = 0;
|
|
20
|
-
let negativeCount = 0;
|
|
21
|
-
let dotCount = 0;
|
|
22
|
-
for (let i = 0, len = num.length; i < len; i++) {
|
|
23
|
-
if (num[i] === ".") {
|
|
24
|
-
dotCount += 1;
|
|
25
|
-
if (dotCount > 1) return false;
|
|
26
|
-
}
|
|
27
|
-
if (/(e|E)+/.test(num[i])) {
|
|
28
|
-
eCount += 1;
|
|
29
|
-
if (eCount > 1) return false;
|
|
30
|
-
}
|
|
31
|
-
if (num[i] === "-") {
|
|
32
|
-
negativeCount += 1;
|
|
33
|
-
if (negativeCount > 2) return false;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// 整数,去除前面的无效 0(本身是 0 除外);小数去除末尾的无效 0
|
|
40
|
-
export function removeInvalidZero(num: string, decimal = false) {
|
|
41
|
-
if (num.indexOf(".") !== -1) {
|
|
42
|
-
log.error("InputNumber", "num is not a integer number.");
|
|
43
|
-
return num;
|
|
44
|
-
}
|
|
45
|
-
if (!num || (num === "0" && decimal)) return "";
|
|
46
|
-
if (num === "0") return num;
|
|
47
|
-
return (decimal ? num.replace(/0+$/, "") : num.replace(/^0+/, "")) || "0";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 大数加法,仅支持正整数(没有精度问题)
|
|
52
|
-
* @param num1 被加数
|
|
53
|
-
* @param num2 加数
|
|
54
|
-
*/
|
|
55
|
-
export function largeIntNumberAdd(num1: string, num2: string, decimal = false): string {
|
|
56
|
-
const number1 = removeInvalidZero(num1, decimal);
|
|
57
|
-
const number2 = removeInvalidZero(num2, decimal);
|
|
58
|
-
const isFirstLarger = number1.length > number2.length;
|
|
59
|
-
const maxNumber = isFirstLarger ? number1 : number2;
|
|
60
|
-
const minNumber = isFirstLarger ? number2 : number1;
|
|
61
|
-
const newNumber: string[] = [];
|
|
62
|
-
const step = [];
|
|
63
|
-
const diff = decimal ? 0 : maxNumber.length - minNumber.length;
|
|
64
|
-
const len = decimal ? minNumber.length : maxNumber.length;
|
|
65
|
-
for (let i = len - 1; i >= 0; i--) {
|
|
66
|
-
const minIndex = i - diff;
|
|
67
|
-
// 第一个数,加第二个数,加进位
|
|
68
|
-
const count = Number(maxNumber[i]) + (Number(minNumber[minIndex]) || 0) + (step[i] || 0);
|
|
69
|
-
if (count >= 10) {
|
|
70
|
-
step[i - 1] = 1;
|
|
71
|
-
}
|
|
72
|
-
newNumber.unshift(String(count % 10));
|
|
73
|
-
}
|
|
74
|
-
// 999 + 1 = 1000,之类的进位
|
|
75
|
-
if (step[-1]) {
|
|
76
|
-
newNumber.unshift("1");
|
|
77
|
-
}
|
|
78
|
-
if (decimal) {
|
|
79
|
-
return newNumber.concat(maxNumber.slice(len, maxNumber.length)).join("");
|
|
80
|
-
}
|
|
81
|
-
return newNumber.join("");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* 大数加法,支持小数和整数(没有精度问题)
|
|
86
|
-
* @param num1 被加数
|
|
87
|
-
* @param num2 加数
|
|
88
|
-
*/
|
|
89
|
-
export function largePositiveNumberAdd(num1: string, num2: string): string {
|
|
90
|
-
const [intNumber1 = "0", decimalNumber1 = "0"] = num1.split(".");
|
|
91
|
-
const [intNumber2 = "0", decimalNumber2 = "0"] = num2.split(".");
|
|
92
|
-
const integerSum = largeIntNumberAdd(intNumber1, intNumber2);
|
|
93
|
-
// 如果不存在小数,则直接返回整数相加结果
|
|
94
|
-
if (decimalNumber1 === "0" && decimalNumber2 === "0") return integerSum;
|
|
95
|
-
const newDecimalNumber1 = removeInvalidZero(decimalNumber1, true);
|
|
96
|
-
const newDecimalNumber2 = removeInvalidZero(decimalNumber2, true);
|
|
97
|
-
// 小数点相加
|
|
98
|
-
const decimalNumberSum = largeIntNumberAdd(newDecimalNumber1, newDecimalNumber2, true);
|
|
99
|
-
// 组合整数部分和小数部分
|
|
100
|
-
const decimalLength = decimalNumberSum.length;
|
|
101
|
-
// 如果小数相加进位
|
|
102
|
-
if (decimalLength > newDecimalNumber1.length && decimalLength > newDecimalNumber2.length) {
|
|
103
|
-
return [removeInvalidZero(largeIntNumberAdd(integerSum, "1")), removeInvalidZero(decimalNumberSum.slice(1), true)]
|
|
104
|
-
.filter((v: string) => v)
|
|
105
|
-
.join(".");
|
|
106
|
-
}
|
|
107
|
-
return [removeInvalidZero(integerSum), removeInvalidZero(decimalNumberSum, true)].filter((v: string) => v).join(".");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* 比较两个大数的大小,仅正整数有效
|
|
112
|
-
*/
|
|
113
|
-
function compareLargeIntegerNumber(num1: string, num2: string): 1 | -1 | 0 {
|
|
114
|
-
const number1 = removeInvalidZero(num1);
|
|
115
|
-
const number2 = removeInvalidZero(num2);
|
|
116
|
-
if (number1.length === number2.length) {
|
|
117
|
-
for (let i = 0, len = number1.length; i < len; i++) {
|
|
118
|
-
if (number1[i] > number2[i]) return 1;
|
|
119
|
-
if (number1[i] < number2[i]) return -1;
|
|
120
|
-
}
|
|
121
|
-
return 0;
|
|
122
|
-
}
|
|
123
|
-
return number1.length > number2.length ? 1 : -1;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function compareLargeDecimalNumber(num1: string, num2: string) {
|
|
127
|
-
const number1 = num1 && num1 !== "0" ? num1.replace(/0+$/, "") : "0";
|
|
128
|
-
const number2 = num2 && num2 !== "0" ? num2.replace(/0+$/, "") : "0";
|
|
129
|
-
const maxLength = Math.max(number1.length, number2.length);
|
|
130
|
-
for (let i = 0, len = maxLength; i < len; i++) {
|
|
131
|
-
if ((number1[i] || 0) > (number2[i] || 0)) return 1;
|
|
132
|
-
if ((number1[i] || 0) < (number2[i] || 0)) return -1;
|
|
133
|
-
}
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* 2e3 => 2000
|
|
139
|
-
* 0.2e3 => 200
|
|
140
|
-
*/
|
|
141
|
-
export function formatENumber(num: string): string {
|
|
142
|
-
const [num1, num2] = num.split("e");
|
|
143
|
-
if (!num2) return num;
|
|
144
|
-
const [integer, initDecimal = ""] = num.split(".");
|
|
145
|
-
const zeroCount = Number(num2);
|
|
146
|
-
const [decimal] = initDecimal.split("e");
|
|
147
|
-
if (zeroCount > decimal.length) {
|
|
148
|
-
const multipleZero = fillZero(zeroCount - decimal.length);
|
|
149
|
-
return num1.replace(/(^0+|\.)/g, "") + multipleZero;
|
|
150
|
-
}
|
|
151
|
-
const n1 = integer.replace(/^0+/, "") + decimal.slice(0, zeroCount);
|
|
152
|
-
const d2 = decimal.slice(zeroCount);
|
|
153
|
-
return d2 ? [n1, d2].join(".") : n1;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* 比较两个大数的大小
|
|
158
|
-
*/
|
|
159
|
-
export function compareLargeNumber(num1: string, num2: string): 1 | -1 | 0 {
|
|
160
|
-
const [integer1, decimal1] = formatENumber(num1).split(".");
|
|
161
|
-
const [integer2, decimal2] = formatENumber(num2).split(".");
|
|
162
|
-
const result = compareLargeIntegerNumber(integer1.replace("-", ""), integer2.replace("-", ""));
|
|
163
|
-
const integer1IsNegative = integer1.includes("-");
|
|
164
|
-
const integer2IsNegative = integer2.includes("-");
|
|
165
|
-
if (integer1IsNegative && !integer2IsNegative) return -1;
|
|
166
|
-
if (!integer1IsNegative && integer2IsNegative) return 1;
|
|
167
|
-
if (integer1IsNegative && integer2IsNegative) {
|
|
168
|
-
if (result === 0) return 0;
|
|
169
|
-
return result > 0 ? -1 : 1;
|
|
170
|
-
}
|
|
171
|
-
if (result === 0) {
|
|
172
|
-
return compareLargeDecimalNumber(decimal1, decimal2);
|
|
173
|
-
}
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// 确认是否为无限大/小
|
|
178
|
-
export function isInfinity(num: number | string) {
|
|
179
|
-
return [-Infinity, Infinity].includes(Number(num));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// 确认是否是大数
|
|
183
|
-
export function isSafeNumber(num: string | number) {
|
|
184
|
-
return Number(num) < Number.MAX_SAFE_INTEGER && Number(num) > Number.MIN_SAFE_INTEGER;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* 比较两个数的大小
|
|
189
|
-
*/
|
|
190
|
-
export function compareNumber(num1: string | number, num2: string | number, largeNumber?: boolean) {
|
|
191
|
-
const isSafeNumberCompare = isSafeNumber(num1) && isSafeNumber(num2) && !largeNumber;
|
|
192
|
-
const isInfinityCompare = isInfinity(num1) || isInfinity(num2);
|
|
193
|
-
if (isSafeNumberCompare || isInfinityCompare) {
|
|
194
|
-
// 比较两个非大数或涉及无穷的大小
|
|
195
|
-
if (Number(num1) === Number(num2)) return 0;
|
|
196
|
-
return Number(num1) > Number(num2) ? 1 : -1;
|
|
197
|
-
}
|
|
198
|
-
// 比较两个大数的大小
|
|
199
|
-
return compareLargeNumber(String(num1), String(num2));
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* 大数减法,仅支持整数
|
|
204
|
-
* @param num1 被减数
|
|
205
|
-
* @param num2 减数
|
|
206
|
-
* @param decimal 是否为小数位相减
|
|
207
|
-
*/
|
|
208
|
-
export function largeIntegerNumberSubtract(
|
|
209
|
-
num1: string,
|
|
210
|
-
num2: string,
|
|
211
|
-
p?: { decimal?: boolean; stayZero?: boolean }
|
|
212
|
-
): string {
|
|
213
|
-
if (num1 === num2) return "0";
|
|
214
|
-
const { decimal, stayZero } = p || {};
|
|
215
|
-
const number1 = removeInvalidZero(num1);
|
|
216
|
-
const number2 = removeInvalidZero(num2);
|
|
217
|
-
const isFirstLarger = compareLargeIntegerNumber(number1, number2) > 0;
|
|
218
|
-
const maxNumber = isFirstLarger ? number1 : number2;
|
|
219
|
-
const minNumber = isFirstLarger ? number2 : number1;
|
|
220
|
-
const newNumber: string[] = [];
|
|
221
|
-
// step 存储借位信息
|
|
222
|
-
const step = [];
|
|
223
|
-
const diff = decimal ? 0 : maxNumber.length - minNumber.length;
|
|
224
|
-
const len = decimal ? minNumber.length : maxNumber.length;
|
|
225
|
-
for (let i = len - 1; i >= 0; i--) {
|
|
226
|
-
const minIndex = i - diff;
|
|
227
|
-
// 第一个数,减第二个数,减借位
|
|
228
|
-
let count = Number(maxNumber[i]) - (Number(minNumber[minIndex]) || 0) - (step[i] || 0);
|
|
229
|
-
if (count < 0) {
|
|
230
|
-
step[i - 1] = 1;
|
|
231
|
-
count += 10;
|
|
232
|
-
}
|
|
233
|
-
newNumber.unshift(String(count));
|
|
234
|
-
}
|
|
235
|
-
if (decimal) {
|
|
236
|
-
return newNumber.concat(maxNumber.slice(len, maxNumber.length)).join("");
|
|
237
|
-
}
|
|
238
|
-
let finalNumber = newNumber.join("");
|
|
239
|
-
if (!stayZero) {
|
|
240
|
-
finalNumber = finalNumber.replace(/^0+/, "");
|
|
241
|
-
}
|
|
242
|
-
return removeInvalidZero(isFirstLarger ? finalNumber : `-${finalNumber}`);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* 大数减法,支持整数和小数(无精度问题)
|
|
247
|
-
* @param num1 被减数
|
|
248
|
-
* @param num2 减数
|
|
249
|
-
* @param decimal 是否为小数位相减
|
|
250
|
-
*/
|
|
251
|
-
export function largePositiveNumberSubtract(num1: string, num2: string): string {
|
|
252
|
-
if (num1 === num2) return "0";
|
|
253
|
-
const isFirstLarger = compareNumber(num1, num2, true) > 0;
|
|
254
|
-
const maxNumber = isFirstLarger ? num1 : num2;
|
|
255
|
-
const minNumber = isFirstLarger ? num2 : num1;
|
|
256
|
-
// 整数部分和小数部分分开处理
|
|
257
|
-
const [intNumber1, decimalNumber1 = "0"] = maxNumber.split(".");
|
|
258
|
-
const [intNumber2, decimalNumber2 = "0"] = minNumber.split(".");
|
|
259
|
-
let integerNumber = largeIntegerNumberSubtract(intNumber1, intNumber2);
|
|
260
|
-
// 如果不存在小数,则直接返回整数相加结果
|
|
261
|
-
if (decimalNumber1 === "0" && decimalNumber2 === "0") {
|
|
262
|
-
return isFirstLarger ? integerNumber : `-${integerNumber}`;
|
|
263
|
-
}
|
|
264
|
-
// 小数点相减
|
|
265
|
-
let decimalNumber = "";
|
|
266
|
-
let addOneNumber = decimalNumber1;
|
|
267
|
-
// 第一个数字的小数位数比第二个少,需补足 0
|
|
268
|
-
if (decimalNumber1.length < decimalNumber2.length) {
|
|
269
|
-
addOneNumber = `${decimalNumber1}${fillZero(decimalNumber2.length - decimalNumber1.length)}`;
|
|
270
|
-
}
|
|
271
|
-
// 第一个小数位更小,是否需要借位
|
|
272
|
-
if (compareLargeDecimalNumber(addOneNumber, decimalNumber2) >= 0) {
|
|
273
|
-
decimalNumber = largeIntegerNumberSubtract(addOneNumber, decimalNumber2, { decimal: true });
|
|
274
|
-
} else {
|
|
275
|
-
if (decimalNumber1.length < decimalNumber2.length || decimalNumber1 === "0") {
|
|
276
|
-
decimalNumber = largeIntegerNumberSubtract(`1${addOneNumber}`, decimalNumber2, { stayZero: true });
|
|
277
|
-
decimalNumber = fillZero(decimalNumber2.length - decimalNumber.length) + decimalNumber;
|
|
278
|
-
} else {
|
|
279
|
-
decimalNumber = largeIntegerNumberSubtract(decimalNumber1, decimalNumber2, { decimal: true });
|
|
280
|
-
}
|
|
281
|
-
integerNumber = largeIntegerNumberSubtract(integerNumber, "1");
|
|
282
|
-
}
|
|
283
|
-
const finalNumber = decimalNumber ? [integerNumber, decimalNumber].join(".") : integerNumber;
|
|
284
|
-
return isFirstLarger ? finalNumber : `-${finalNumber}`;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* -0.6 - 0.8 => -(0.6 + 0.8)
|
|
289
|
-
* -0.6 - (-0.8) => 0.8 - 0.6
|
|
290
|
-
* 0.6 - (-0.8) => 0.6 + 0.8
|
|
291
|
-
* 0.6 - 0.8 => 0.6 - 0.8
|
|
292
|
-
*/
|
|
293
|
-
export function largeNumberSubtract(num1: string, num2: string): string {
|
|
294
|
-
const isFirstNegative = num1[0] === "-";
|
|
295
|
-
const isSecondNegative = num2[0] === "-";
|
|
296
|
-
if (isFirstNegative && !isSecondNegative) {
|
|
297
|
-
const r = largePositiveNumberAdd(num1.slice(1), num2);
|
|
298
|
-
return `-${r}`;
|
|
299
|
-
}
|
|
300
|
-
if (isFirstNegative && isSecondNegative) {
|
|
301
|
-
return largePositiveNumberSubtract(num2.slice(1), num1.slice(1));
|
|
302
|
-
}
|
|
303
|
-
if (!isFirstNegative && isSecondNegative) {
|
|
304
|
-
return largePositiveNumberAdd(num1, num2.slice(1));
|
|
305
|
-
}
|
|
306
|
-
return largePositiveNumberSubtract(num1, num2);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* -0.6 + 0.8 => 0.8 - 0.6
|
|
311
|
-
* -0.6 + (-0.8) => -(0.6 + 0.8)
|
|
312
|
-
* 0.6 + (-0.8) => 0.6 - 0.8
|
|
313
|
-
* 0.6 + 0.8 => 0.6 + 0.8
|
|
314
|
-
*/
|
|
315
|
-
export function largeNumberAdd(num1: string, num2: string): string {
|
|
316
|
-
const isFirstNegative = num1[0] === "-";
|
|
317
|
-
const isSecondNegative = num2[0] === "-";
|
|
318
|
-
if (isFirstNegative && !isSecondNegative) {
|
|
319
|
-
return largePositiveNumberSubtract(num2, num1.slice(1));
|
|
320
|
-
}
|
|
321
|
-
if (isFirstNegative && isSecondNegative) {
|
|
322
|
-
const r = largePositiveNumberAdd(num2.slice(1), num1.slice(1));
|
|
323
|
-
return `-${r}`;
|
|
324
|
-
}
|
|
325
|
-
if (!isFirstNegative && isSecondNegative) {
|
|
326
|
-
return largePositiveNumberSubtract(num1, num2.slice(1));
|
|
327
|
-
}
|
|
328
|
-
return largePositiveNumberAdd(num1, num2);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* 格式化小数,并且可以控制小数点后的位数和是否进行四舍五入。
|
|
333
|
-
*
|
|
334
|
-
* @param {number} num - 要格式化的数字。
|
|
335
|
-
* @param {number} places - 小数点后的位数。
|
|
336
|
-
* @param {boolean} rounding - 是否进行四舍五入。
|
|
337
|
-
* @returns {string} 格式化后的数字字符串。
|
|
338
|
-
*/
|
|
339
|
-
export function formatDecimal(num: number, places: number, enableRound: boolean = true) {
|
|
340
|
-
// 开启四舍五入 直接用 toFixed
|
|
341
|
-
if (enableRound) {
|
|
342
|
-
return num.toFixed(places);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const [integer, decimal] = num.toString().split(".");
|
|
346
|
-
// 保留 0 位小数
|
|
347
|
-
if (places === 0) {
|
|
348
|
-
return integer;
|
|
349
|
-
}
|
|
350
|
-
// 补足小数位数
|
|
351
|
-
if (decimal) {
|
|
352
|
-
let decimalNumber = decimal.slice(0, places);
|
|
353
|
-
if (decimal.length < places) {
|
|
354
|
-
decimalNumber += fillZero(places - decimal.length);
|
|
355
|
-
}
|
|
356
|
-
return [integer, decimalNumber].join(".");
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return [integer, fillZero(places)].join(".");
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
export function decimalPlacesToFixedNum(num: number, decimalPlaces: InputNumberDecimalPlaces) {
|
|
363
|
-
if (isObject(decimalPlaces)) {
|
|
364
|
-
return formatDecimal(num, decimalPlaces.places, decimalPlaces.enableRound ?? true);
|
|
365
|
-
}
|
|
366
|
-
return formatDecimal(num, decimalPlaces, true);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* 大数保留 N 位小数(没有精度问题)
|
|
371
|
-
* @param {String} number 大数(只能使用字符串表示)
|
|
372
|
-
* @param {Number} decimalPlaces 保留的小数位数
|
|
373
|
-
* @param {Boolean} largeNumber 是否为大数
|
|
374
|
-
*/
|
|
375
|
-
export function largeNumberToFixed(
|
|
376
|
-
number: string | number,
|
|
377
|
-
decimalPlaces: InputNumberDecimalPlaces = 0,
|
|
378
|
-
largeNumber: boolean = true
|
|
379
|
-
): string {
|
|
380
|
-
if (Number.isNaN(Number(number))) return "";
|
|
381
|
-
if (!largeNumber) {
|
|
382
|
-
return decimalPlacesToFixedNum(Number(number), decimalPlaces);
|
|
383
|
-
}
|
|
384
|
-
const places = isObject(decimalPlaces) ? decimalPlaces.places : decimalPlaces;
|
|
385
|
-
const enableRound = isObject(decimalPlaces) ? decimalPlaces.enableRound ?? true : true;
|
|
386
|
-
if (!isString(number)) return String(number);
|
|
387
|
-
|
|
388
|
-
let [num1, num2] = number.split(".");
|
|
389
|
-
// 如果不存在小数点,则补足位数
|
|
390
|
-
if (!num2) {
|
|
391
|
-
return places > 0 && enableRound ? [number, fillZero(places)].join(".") : number;
|
|
392
|
-
}
|
|
393
|
-
// 存在小数点,保留 0 位小数,灵活配置四舍五入
|
|
394
|
-
if (places === 0) {
|
|
395
|
-
return enableRound && Number(num2[0]) >= 5 ? largePositiveNumberAdd(num1, "1") : num1;
|
|
396
|
-
}
|
|
397
|
-
// 存在小数点,保留 > 0 位小数,灵活配置四舍五入
|
|
398
|
-
let decimalNumber = num2.slice(0, places);
|
|
399
|
-
if (num2.length < places) {
|
|
400
|
-
decimalNumber += fillZero(places - num2.length);
|
|
401
|
-
} else if (enableRound) {
|
|
402
|
-
// 用于判断是否处于 1.08 这种小数为0开始的边界情况
|
|
403
|
-
const leadZeroNum = decimalNumber.match(/^0+/)?.[0].length;
|
|
404
|
-
// 用于判断是否处于 0.99/1.99 等需要往非0位进位的场景
|
|
405
|
-
const leadNineNum = decimalNumber.match(/^9+/);
|
|
406
|
-
// 决定是否需要四舍五入
|
|
407
|
-
const needAdded = Number(num2[places]) >= 5;
|
|
408
|
-
|
|
409
|
-
// 四舍五入后的结果
|
|
410
|
-
decimalNumber = needAdded ? largePositiveNumberAdd(decimalNumber, "1") : decimalNumber;
|
|
411
|
-
|
|
412
|
-
// 边界场景1(1.08 这种小数为0开始的边界情况):计算后有误判的可能,如008 +1 误判为 8+1,需要手动补 0
|
|
413
|
-
if (leadZeroNum && needAdded && leadZeroNum + decimalNumber.length >= places) {
|
|
414
|
-
decimalNumber = `${fillZero(places - decimalNumber.length)}${decimalNumber}`;
|
|
415
|
-
}
|
|
416
|
-
// 边界场景2:(0.99 这种可能进位的边界情况):计算后有误判的可能,如995 四舍五入后需进位
|
|
417
|
-
if (leadNineNum && decimalNumber.length > places) {
|
|
418
|
-
num1 = (Number(num1) + 1).toString();
|
|
419
|
-
decimalNumber = fillZero(places);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
return [num1, decimalNumber].join(".");
|
|
423
|
-
}
|
|
1
|
+
import { isString, isNumber, isObject } from "lodash-es";
|
|
2
|
+
import log from "../log/log";
|
|
3
|
+
|
|
4
|
+
export type InputNumberDecimalPlaces = number | { enableRound: boolean; places: number };
|
|
5
|
+
|
|
6
|
+
export function fillZero(length: number) {
|
|
7
|
+
return new Array(length).fill(0).join("");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 大数,是否是一个数字,数字字符包括 - . e [0-9]
|
|
12
|
+
*/
|
|
13
|
+
export function isInputNumber(num: number | string): boolean {
|
|
14
|
+
if (!num) return true;
|
|
15
|
+
if (isNumber(num)) return !Number.isNaN(num);
|
|
16
|
+
const r = /^[0-9|e|E|-]+\.*[0-9|e|E|-]*$/.test(num);
|
|
17
|
+
if (!r) return false;
|
|
18
|
+
// only allow one [.e] and two [-]
|
|
19
|
+
let eCount = 0;
|
|
20
|
+
let negativeCount = 0;
|
|
21
|
+
let dotCount = 0;
|
|
22
|
+
for (let i = 0, len = num.length; i < len; i++) {
|
|
23
|
+
if (num[i] === ".") {
|
|
24
|
+
dotCount += 1;
|
|
25
|
+
if (dotCount > 1) return false;
|
|
26
|
+
}
|
|
27
|
+
if (/(e|E)+/.test(num[i])) {
|
|
28
|
+
eCount += 1;
|
|
29
|
+
if (eCount > 1) return false;
|
|
30
|
+
}
|
|
31
|
+
if (num[i] === "-") {
|
|
32
|
+
negativeCount += 1;
|
|
33
|
+
if (negativeCount > 2) return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 整数,去除前面的无效 0(本身是 0 除外);小数去除末尾的无效 0
|
|
40
|
+
export function removeInvalidZero(num: string, decimal = false) {
|
|
41
|
+
if (num.indexOf(".") !== -1) {
|
|
42
|
+
log.error("InputNumber", "num is not a integer number.");
|
|
43
|
+
return num;
|
|
44
|
+
}
|
|
45
|
+
if (!num || (num === "0" && decimal)) return "";
|
|
46
|
+
if (num === "0") return num;
|
|
47
|
+
return (decimal ? num.replace(/0+$/, "") : num.replace(/^0+/, "")) || "0";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 大数加法,仅支持正整数(没有精度问题)
|
|
52
|
+
* @param num1 被加数
|
|
53
|
+
* @param num2 加数
|
|
54
|
+
*/
|
|
55
|
+
export function largeIntNumberAdd(num1: string, num2: string, decimal = false): string {
|
|
56
|
+
const number1 = removeInvalidZero(num1, decimal);
|
|
57
|
+
const number2 = removeInvalidZero(num2, decimal);
|
|
58
|
+
const isFirstLarger = number1.length > number2.length;
|
|
59
|
+
const maxNumber = isFirstLarger ? number1 : number2;
|
|
60
|
+
const minNumber = isFirstLarger ? number2 : number1;
|
|
61
|
+
const newNumber: string[] = [];
|
|
62
|
+
const step = [];
|
|
63
|
+
const diff = decimal ? 0 : maxNumber.length - minNumber.length;
|
|
64
|
+
const len = decimal ? minNumber.length : maxNumber.length;
|
|
65
|
+
for (let i = len - 1; i >= 0; i--) {
|
|
66
|
+
const minIndex = i - diff;
|
|
67
|
+
// 第一个数,加第二个数,加进位
|
|
68
|
+
const count = Number(maxNumber[i]) + (Number(minNumber[minIndex]) || 0) + (step[i] || 0);
|
|
69
|
+
if (count >= 10) {
|
|
70
|
+
step[i - 1] = 1;
|
|
71
|
+
}
|
|
72
|
+
newNumber.unshift(String(count % 10));
|
|
73
|
+
}
|
|
74
|
+
// 999 + 1 = 1000,之类的进位
|
|
75
|
+
if (step[-1]) {
|
|
76
|
+
newNumber.unshift("1");
|
|
77
|
+
}
|
|
78
|
+
if (decimal) {
|
|
79
|
+
return newNumber.concat(maxNumber.slice(len, maxNumber.length)).join("");
|
|
80
|
+
}
|
|
81
|
+
return newNumber.join("");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 大数加法,支持小数和整数(没有精度问题)
|
|
86
|
+
* @param num1 被加数
|
|
87
|
+
* @param num2 加数
|
|
88
|
+
*/
|
|
89
|
+
export function largePositiveNumberAdd(num1: string, num2: string): string {
|
|
90
|
+
const [intNumber1 = "0", decimalNumber1 = "0"] = num1.split(".");
|
|
91
|
+
const [intNumber2 = "0", decimalNumber2 = "0"] = num2.split(".");
|
|
92
|
+
const integerSum = largeIntNumberAdd(intNumber1, intNumber2);
|
|
93
|
+
// 如果不存在小数,则直接返回整数相加结果
|
|
94
|
+
if (decimalNumber1 === "0" && decimalNumber2 === "0") return integerSum;
|
|
95
|
+
const newDecimalNumber1 = removeInvalidZero(decimalNumber1, true);
|
|
96
|
+
const newDecimalNumber2 = removeInvalidZero(decimalNumber2, true);
|
|
97
|
+
// 小数点相加
|
|
98
|
+
const decimalNumberSum = largeIntNumberAdd(newDecimalNumber1, newDecimalNumber2, true);
|
|
99
|
+
// 组合整数部分和小数部分
|
|
100
|
+
const decimalLength = decimalNumberSum.length;
|
|
101
|
+
// 如果小数相加进位
|
|
102
|
+
if (decimalLength > newDecimalNumber1.length && decimalLength > newDecimalNumber2.length) {
|
|
103
|
+
return [removeInvalidZero(largeIntNumberAdd(integerSum, "1")), removeInvalidZero(decimalNumberSum.slice(1), true)]
|
|
104
|
+
.filter((v: string) => v)
|
|
105
|
+
.join(".");
|
|
106
|
+
}
|
|
107
|
+
return [removeInvalidZero(integerSum), removeInvalidZero(decimalNumberSum, true)].filter((v: string) => v).join(".");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 比较两个大数的大小,仅正整数有效
|
|
112
|
+
*/
|
|
113
|
+
function compareLargeIntegerNumber(num1: string, num2: string): 1 | -1 | 0 {
|
|
114
|
+
const number1 = removeInvalidZero(num1);
|
|
115
|
+
const number2 = removeInvalidZero(num2);
|
|
116
|
+
if (number1.length === number2.length) {
|
|
117
|
+
for (let i = 0, len = number1.length; i < len; i++) {
|
|
118
|
+
if (number1[i] > number2[i]) return 1;
|
|
119
|
+
if (number1[i] < number2[i]) return -1;
|
|
120
|
+
}
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
return number1.length > number2.length ? 1 : -1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function compareLargeDecimalNumber(num1: string, num2: string) {
|
|
127
|
+
const number1 = num1 && num1 !== "0" ? num1.replace(/0+$/, "") : "0";
|
|
128
|
+
const number2 = num2 && num2 !== "0" ? num2.replace(/0+$/, "") : "0";
|
|
129
|
+
const maxLength = Math.max(number1.length, number2.length);
|
|
130
|
+
for (let i = 0, len = maxLength; i < len; i++) {
|
|
131
|
+
if ((number1[i] || 0) > (number2[i] || 0)) return 1;
|
|
132
|
+
if ((number1[i] || 0) < (number2[i] || 0)) return -1;
|
|
133
|
+
}
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 2e3 => 2000
|
|
139
|
+
* 0.2e3 => 200
|
|
140
|
+
*/
|
|
141
|
+
export function formatENumber(num: string): string {
|
|
142
|
+
const [num1, num2] = num.split("e");
|
|
143
|
+
if (!num2) return num;
|
|
144
|
+
const [integer, initDecimal = ""] = num.split(".");
|
|
145
|
+
const zeroCount = Number(num2);
|
|
146
|
+
const [decimal] = initDecimal.split("e");
|
|
147
|
+
if (zeroCount > decimal.length) {
|
|
148
|
+
const multipleZero = fillZero(zeroCount - decimal.length);
|
|
149
|
+
return num1.replace(/(^0+|\.)/g, "") + multipleZero;
|
|
150
|
+
}
|
|
151
|
+
const n1 = integer.replace(/^0+/, "") + decimal.slice(0, zeroCount);
|
|
152
|
+
const d2 = decimal.slice(zeroCount);
|
|
153
|
+
return d2 ? [n1, d2].join(".") : n1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 比较两个大数的大小
|
|
158
|
+
*/
|
|
159
|
+
export function compareLargeNumber(num1: string, num2: string): 1 | -1 | 0 {
|
|
160
|
+
const [integer1, decimal1] = formatENumber(num1).split(".");
|
|
161
|
+
const [integer2, decimal2] = formatENumber(num2).split(".");
|
|
162
|
+
const result = compareLargeIntegerNumber(integer1.replace("-", ""), integer2.replace("-", ""));
|
|
163
|
+
const integer1IsNegative = integer1.includes("-");
|
|
164
|
+
const integer2IsNegative = integer2.includes("-");
|
|
165
|
+
if (integer1IsNegative && !integer2IsNegative) return -1;
|
|
166
|
+
if (!integer1IsNegative && integer2IsNegative) return 1;
|
|
167
|
+
if (integer1IsNegative && integer2IsNegative) {
|
|
168
|
+
if (result === 0) return 0;
|
|
169
|
+
return result > 0 ? -1 : 1;
|
|
170
|
+
}
|
|
171
|
+
if (result === 0) {
|
|
172
|
+
return compareLargeDecimalNumber(decimal1, decimal2);
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 确认是否为无限大/小
|
|
178
|
+
export function isInfinity(num: number | string) {
|
|
179
|
+
return [-Infinity, Infinity].includes(Number(num));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 确认是否是大数
|
|
183
|
+
export function isSafeNumber(num: string | number) {
|
|
184
|
+
return Number(num) < Number.MAX_SAFE_INTEGER && Number(num) > Number.MIN_SAFE_INTEGER;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 比较两个数的大小
|
|
189
|
+
*/
|
|
190
|
+
export function compareNumber(num1: string | number, num2: string | number, largeNumber?: boolean) {
|
|
191
|
+
const isSafeNumberCompare = isSafeNumber(num1) && isSafeNumber(num2) && !largeNumber;
|
|
192
|
+
const isInfinityCompare = isInfinity(num1) || isInfinity(num2);
|
|
193
|
+
if (isSafeNumberCompare || isInfinityCompare) {
|
|
194
|
+
// 比较两个非大数或涉及无穷的大小
|
|
195
|
+
if (Number(num1) === Number(num2)) return 0;
|
|
196
|
+
return Number(num1) > Number(num2) ? 1 : -1;
|
|
197
|
+
}
|
|
198
|
+
// 比较两个大数的大小
|
|
199
|
+
return compareLargeNumber(String(num1), String(num2));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 大数减法,仅支持整数
|
|
204
|
+
* @param num1 被减数
|
|
205
|
+
* @param num2 减数
|
|
206
|
+
* @param decimal 是否为小数位相减
|
|
207
|
+
*/
|
|
208
|
+
export function largeIntegerNumberSubtract(
|
|
209
|
+
num1: string,
|
|
210
|
+
num2: string,
|
|
211
|
+
p?: { decimal?: boolean; stayZero?: boolean }
|
|
212
|
+
): string {
|
|
213
|
+
if (num1 === num2) return "0";
|
|
214
|
+
const { decimal, stayZero } = p || {};
|
|
215
|
+
const number1 = removeInvalidZero(num1);
|
|
216
|
+
const number2 = removeInvalidZero(num2);
|
|
217
|
+
const isFirstLarger = compareLargeIntegerNumber(number1, number2) > 0;
|
|
218
|
+
const maxNumber = isFirstLarger ? number1 : number2;
|
|
219
|
+
const minNumber = isFirstLarger ? number2 : number1;
|
|
220
|
+
const newNumber: string[] = [];
|
|
221
|
+
// step 存储借位信息
|
|
222
|
+
const step = [];
|
|
223
|
+
const diff = decimal ? 0 : maxNumber.length - minNumber.length;
|
|
224
|
+
const len = decimal ? minNumber.length : maxNumber.length;
|
|
225
|
+
for (let i = len - 1; i >= 0; i--) {
|
|
226
|
+
const minIndex = i - diff;
|
|
227
|
+
// 第一个数,减第二个数,减借位
|
|
228
|
+
let count = Number(maxNumber[i]) - (Number(minNumber[minIndex]) || 0) - (step[i] || 0);
|
|
229
|
+
if (count < 0) {
|
|
230
|
+
step[i - 1] = 1;
|
|
231
|
+
count += 10;
|
|
232
|
+
}
|
|
233
|
+
newNumber.unshift(String(count));
|
|
234
|
+
}
|
|
235
|
+
if (decimal) {
|
|
236
|
+
return newNumber.concat(maxNumber.slice(len, maxNumber.length)).join("");
|
|
237
|
+
}
|
|
238
|
+
let finalNumber = newNumber.join("");
|
|
239
|
+
if (!stayZero) {
|
|
240
|
+
finalNumber = finalNumber.replace(/^0+/, "");
|
|
241
|
+
}
|
|
242
|
+
return removeInvalidZero(isFirstLarger ? finalNumber : `-${finalNumber}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 大数减法,支持整数和小数(无精度问题)
|
|
247
|
+
* @param num1 被减数
|
|
248
|
+
* @param num2 减数
|
|
249
|
+
* @param decimal 是否为小数位相减
|
|
250
|
+
*/
|
|
251
|
+
export function largePositiveNumberSubtract(num1: string, num2: string): string {
|
|
252
|
+
if (num1 === num2) return "0";
|
|
253
|
+
const isFirstLarger = compareNumber(num1, num2, true) > 0;
|
|
254
|
+
const maxNumber = isFirstLarger ? num1 : num2;
|
|
255
|
+
const minNumber = isFirstLarger ? num2 : num1;
|
|
256
|
+
// 整数部分和小数部分分开处理
|
|
257
|
+
const [intNumber1, decimalNumber1 = "0"] = maxNumber.split(".");
|
|
258
|
+
const [intNumber2, decimalNumber2 = "0"] = minNumber.split(".");
|
|
259
|
+
let integerNumber = largeIntegerNumberSubtract(intNumber1, intNumber2);
|
|
260
|
+
// 如果不存在小数,则直接返回整数相加结果
|
|
261
|
+
if (decimalNumber1 === "0" && decimalNumber2 === "0") {
|
|
262
|
+
return isFirstLarger ? integerNumber : `-${integerNumber}`;
|
|
263
|
+
}
|
|
264
|
+
// 小数点相减
|
|
265
|
+
let decimalNumber = "";
|
|
266
|
+
let addOneNumber = decimalNumber1;
|
|
267
|
+
// 第一个数字的小数位数比第二个少,需补足 0
|
|
268
|
+
if (decimalNumber1.length < decimalNumber2.length) {
|
|
269
|
+
addOneNumber = `${decimalNumber1}${fillZero(decimalNumber2.length - decimalNumber1.length)}`;
|
|
270
|
+
}
|
|
271
|
+
// 第一个小数位更小,是否需要借位
|
|
272
|
+
if (compareLargeDecimalNumber(addOneNumber, decimalNumber2) >= 0) {
|
|
273
|
+
decimalNumber = largeIntegerNumberSubtract(addOneNumber, decimalNumber2, { decimal: true });
|
|
274
|
+
} else {
|
|
275
|
+
if (decimalNumber1.length < decimalNumber2.length || decimalNumber1 === "0") {
|
|
276
|
+
decimalNumber = largeIntegerNumberSubtract(`1${addOneNumber}`, decimalNumber2, { stayZero: true });
|
|
277
|
+
decimalNumber = fillZero(decimalNumber2.length - decimalNumber.length) + decimalNumber;
|
|
278
|
+
} else {
|
|
279
|
+
decimalNumber = largeIntegerNumberSubtract(decimalNumber1, decimalNumber2, { decimal: true });
|
|
280
|
+
}
|
|
281
|
+
integerNumber = largeIntegerNumberSubtract(integerNumber, "1");
|
|
282
|
+
}
|
|
283
|
+
const finalNumber = decimalNumber ? [integerNumber, decimalNumber].join(".") : integerNumber;
|
|
284
|
+
return isFirstLarger ? finalNumber : `-${finalNumber}`;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* -0.6 - 0.8 => -(0.6 + 0.8)
|
|
289
|
+
* -0.6 - (-0.8) => 0.8 - 0.6
|
|
290
|
+
* 0.6 - (-0.8) => 0.6 + 0.8
|
|
291
|
+
* 0.6 - 0.8 => 0.6 - 0.8
|
|
292
|
+
*/
|
|
293
|
+
export function largeNumberSubtract(num1: string, num2: string): string {
|
|
294
|
+
const isFirstNegative = num1[0] === "-";
|
|
295
|
+
const isSecondNegative = num2[0] === "-";
|
|
296
|
+
if (isFirstNegative && !isSecondNegative) {
|
|
297
|
+
const r = largePositiveNumberAdd(num1.slice(1), num2);
|
|
298
|
+
return `-${r}`;
|
|
299
|
+
}
|
|
300
|
+
if (isFirstNegative && isSecondNegative) {
|
|
301
|
+
return largePositiveNumberSubtract(num2.slice(1), num1.slice(1));
|
|
302
|
+
}
|
|
303
|
+
if (!isFirstNegative && isSecondNegative) {
|
|
304
|
+
return largePositiveNumberAdd(num1, num2.slice(1));
|
|
305
|
+
}
|
|
306
|
+
return largePositiveNumberSubtract(num1, num2);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* -0.6 + 0.8 => 0.8 - 0.6
|
|
311
|
+
* -0.6 + (-0.8) => -(0.6 + 0.8)
|
|
312
|
+
* 0.6 + (-0.8) => 0.6 - 0.8
|
|
313
|
+
* 0.6 + 0.8 => 0.6 + 0.8
|
|
314
|
+
*/
|
|
315
|
+
export function largeNumberAdd(num1: string, num2: string): string {
|
|
316
|
+
const isFirstNegative = num1[0] === "-";
|
|
317
|
+
const isSecondNegative = num2[0] === "-";
|
|
318
|
+
if (isFirstNegative && !isSecondNegative) {
|
|
319
|
+
return largePositiveNumberSubtract(num2, num1.slice(1));
|
|
320
|
+
}
|
|
321
|
+
if (isFirstNegative && isSecondNegative) {
|
|
322
|
+
const r = largePositiveNumberAdd(num2.slice(1), num1.slice(1));
|
|
323
|
+
return `-${r}`;
|
|
324
|
+
}
|
|
325
|
+
if (!isFirstNegative && isSecondNegative) {
|
|
326
|
+
return largePositiveNumberSubtract(num1, num2.slice(1));
|
|
327
|
+
}
|
|
328
|
+
return largePositiveNumberAdd(num1, num2);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* 格式化小数,并且可以控制小数点后的位数和是否进行四舍五入。
|
|
333
|
+
*
|
|
334
|
+
* @param {number} num - 要格式化的数字。
|
|
335
|
+
* @param {number} places - 小数点后的位数。
|
|
336
|
+
* @param {boolean} rounding - 是否进行四舍五入。
|
|
337
|
+
* @returns {string} 格式化后的数字字符串。
|
|
338
|
+
*/
|
|
339
|
+
export function formatDecimal(num: number, places: number, enableRound: boolean = true) {
|
|
340
|
+
// 开启四舍五入 直接用 toFixed
|
|
341
|
+
if (enableRound) {
|
|
342
|
+
return num.toFixed(places);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const [integer, decimal] = num.toString().split(".");
|
|
346
|
+
// 保留 0 位小数
|
|
347
|
+
if (places === 0) {
|
|
348
|
+
return integer;
|
|
349
|
+
}
|
|
350
|
+
// 补足小数位数
|
|
351
|
+
if (decimal) {
|
|
352
|
+
let decimalNumber = decimal.slice(0, places);
|
|
353
|
+
if (decimal.length < places) {
|
|
354
|
+
decimalNumber += fillZero(places - decimal.length);
|
|
355
|
+
}
|
|
356
|
+
return [integer, decimalNumber].join(".");
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return [integer, fillZero(places)].join(".");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function decimalPlacesToFixedNum(num: number, decimalPlaces: InputNumberDecimalPlaces) {
|
|
363
|
+
if (isObject(decimalPlaces)) {
|
|
364
|
+
return formatDecimal(num, decimalPlaces.places, decimalPlaces.enableRound ?? true);
|
|
365
|
+
}
|
|
366
|
+
return formatDecimal(num, decimalPlaces, true);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* 大数保留 N 位小数(没有精度问题)
|
|
371
|
+
* @param {String} number 大数(只能使用字符串表示)
|
|
372
|
+
* @param {Number} decimalPlaces 保留的小数位数
|
|
373
|
+
* @param {Boolean} largeNumber 是否为大数
|
|
374
|
+
*/
|
|
375
|
+
export function largeNumberToFixed(
|
|
376
|
+
number: string | number,
|
|
377
|
+
decimalPlaces: InputNumberDecimalPlaces = 0,
|
|
378
|
+
largeNumber: boolean = true
|
|
379
|
+
): string {
|
|
380
|
+
if (Number.isNaN(Number(number))) return "";
|
|
381
|
+
if (!largeNumber) {
|
|
382
|
+
return decimalPlacesToFixedNum(Number(number), decimalPlaces);
|
|
383
|
+
}
|
|
384
|
+
const places = isObject(decimalPlaces) ? decimalPlaces.places : decimalPlaces;
|
|
385
|
+
const enableRound = isObject(decimalPlaces) ? decimalPlaces.enableRound ?? true : true;
|
|
386
|
+
if (!isString(number)) return String(number);
|
|
387
|
+
|
|
388
|
+
let [num1, num2] = number.split(".");
|
|
389
|
+
// 如果不存在小数点,则补足位数
|
|
390
|
+
if (!num2) {
|
|
391
|
+
return places > 0 && enableRound ? [number, fillZero(places)].join(".") : number;
|
|
392
|
+
}
|
|
393
|
+
// 存在小数点,保留 0 位小数,灵活配置四舍五入
|
|
394
|
+
if (places === 0) {
|
|
395
|
+
return enableRound && Number(num2[0]) >= 5 ? largePositiveNumberAdd(num1, "1") : num1;
|
|
396
|
+
}
|
|
397
|
+
// 存在小数点,保留 > 0 位小数,灵活配置四舍五入
|
|
398
|
+
let decimalNumber = num2.slice(0, places);
|
|
399
|
+
if (num2.length < places) {
|
|
400
|
+
decimalNumber += fillZero(places - num2.length);
|
|
401
|
+
} else if (enableRound) {
|
|
402
|
+
// 用于判断是否处于 1.08 这种小数为0开始的边界情况
|
|
403
|
+
const leadZeroNum = decimalNumber.match(/^0+/)?.[0].length;
|
|
404
|
+
// 用于判断是否处于 0.99/1.99 等需要往非0位进位的场景
|
|
405
|
+
const leadNineNum = decimalNumber.match(/^9+/);
|
|
406
|
+
// 决定是否需要四舍五入
|
|
407
|
+
const needAdded = Number(num2[places]) >= 5;
|
|
408
|
+
|
|
409
|
+
// 四舍五入后的结果
|
|
410
|
+
decimalNumber = needAdded ? largePositiveNumberAdd(decimalNumber, "1") : decimalNumber;
|
|
411
|
+
|
|
412
|
+
// 边界场景1(1.08 这种小数为0开始的边界情况):计算后有误判的可能,如008 +1 误判为 8+1,需要手动补 0
|
|
413
|
+
if (leadZeroNum && needAdded && leadZeroNum + decimalNumber.length >= places) {
|
|
414
|
+
decimalNumber = `${fillZero(places - decimalNumber.length)}${decimalNumber}`;
|
|
415
|
+
}
|
|
416
|
+
// 边界场景2:(0.99 这种可能进位的边界情况):计算后有误判的可能,如995 四舍五入后需进位
|
|
417
|
+
if (leadNineNum && decimalNumber.length > places) {
|
|
418
|
+
num1 = (Number(num1) + 1).toString();
|
|
419
|
+
decimalNumber = fillZero(places);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return [num1, decimalNumber].join(".");
|
|
423
|
+
}
|