From 14a4ac015c32c77b86451bb57781e91e0b12ed8f Mon Sep 17 00:00:00 2001 From: JuctTr <1301842163@qq.com> Date: Wed, 16 Aug 2023 11:01:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0number=20package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +- .vscode/launch.json | 23 ++ .vscode/settings.json | 7 + lerna.json | 11 +- packages/number/README.md | 15 + packages/number/__test__/addComma.test.ts | 34 +++ packages/number/__test__/operation.spec.ts | 326 +++++++++++++++++++++ packages/number/package.json | 39 +++ packages/number/src/addComma.ts | 19 ++ packages/number/src/index.ts | 6 + packages/number/src/operation.ts | 232 +++++++++++++++ packages/number/tsconfig.json | 5 + packages/number/vite.config.ts | 9 + scripts/create-new-package.js | 7 + 14 files changed, 735 insertions(+), 4 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 packages/number/README.md create mode 100644 packages/number/__test__/addComma.test.ts create mode 100644 packages/number/__test__/operation.spec.ts create mode 100644 packages/number/package.json create mode 100644 packages/number/src/addComma.ts create mode 100644 packages/number/src/index.ts create mode 100644 packages/number/src/operation.ts create mode 100644 packages/number/tsconfig.json create mode 100644 packages/number/vite.config.ts create mode 100644 scripts/create-new-package.js diff --git a/.gitignore b/.gitignore index ea8681f..586e3a4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ dist-ssr *.local # Editor directories and files -.vscode/* +# .vscode/* !.vscode/extensions.json .idea .DS_Store @@ -31,3 +31,7 @@ web-docs/**/**/cache/* packages/**/cjs/* packages/**/es/* + + +packages/storage +packages/datatime diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..dfed774 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "test2 vite build", + "request": "launch", + "runtimeArgs": ["build"], + "runtimeExecutable": "pnpm", + "skipFiles": ["/**"], + "type": "node" + }, + { + "type": "node", + "request": "launch", + "name": "启动程序", + "skipFiles": ["/**"], + "program": "${file}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7d2e951 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": true + } +} diff --git a/lerna.json b/lerna.json index 0ab4311..15d7d38 100644 --- a/lerna.json +++ b/lerna.json @@ -1,16 +1,21 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "useWorkspaces": true, "npmClient": "pnpm", "version": "independent", "command": { "version": { - "allowBranch": ["main", "master"], + "allowBranch": [ + "main", + "master" + ], "conventionalCommits": true, "message": "chore(release): publish %s" }, "publish": { - "allowBranch": ["main", "master"], + "allowBranch": [ + "main", + "master" + ], "conventionalCommits": true } } diff --git a/packages/number/README.md b/packages/number/README.md new file mode 100644 index 0000000..221bc39 --- /dev/null +++ b/packages/number/README.md @@ -0,0 +1,15 @@ +## 描述 + +解决浮动运算问题,避免小数点后产生多位数和计算精度丢失,使得 javascript 能够精确执行加、减、乘、除运算。 + +## 安装 + +```bash +npm i -S @jucttr/number --registry=https://registry.npmjs.org/ +``` + +## 使用 + +## 参考 + +https://github.com/nefe/number-precision diff --git a/packages/number/__test__/addComma.test.ts b/packages/number/__test__/addComma.test.ts new file mode 100644 index 0000000..ba93367 --- /dev/null +++ b/packages/number/__test__/addComma.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, test } from 'vitest' +import { addComma } from '../src/addComma' + +describe('addComma', () => { + test('非数字', () => { + expect(addComma(NaN)).toBe('NaN') + }) + + test('测试正整数', () => { + expect(addComma(5)).toBe('5') + expect(addComma(95)).toBe('95') + expect(addComma(995)).toBe('995') + expect(addComma(1995)).toBe('1,995') + expect(addComma(9945995)).toBe('9,945,995') + expect(addComma(123349945995)).toBe('123,349,945,995') + }) + + test('负正整数', () => { + expect(addComma(-1)).toBe('-1') + expect(addComma(-123)).toBe('-123') + expect(addComma(-1234)).toBe('-1,234') + expect(addComma(-9945995)).toBe('-9,945,995') + }) + + test('小数', () => { + expect(addComma(1.1)).toBe('1.1') + expect(addComma(1.11)).toBe('1.11') + expect(addComma(1.111)).toBe('1.111') + expect(addComma(1.1111)).toBe('1.1111') + expect(addComma(11111.11111)).toBe('11,111.11111') + expect(addComma(1234.11111)).toBe('1,234.11111') + expect(addComma(-12345.11111)).toBe('-12,345.11111') + }) +}) diff --git a/packages/number/__test__/operation.spec.ts b/packages/number/__test__/operation.spec.ts new file mode 100644 index 0000000..ccc7ce6 --- /dev/null +++ b/packages/number/__test__/operation.spec.ts @@ -0,0 +1,326 @@ +import { describe, expect, test } from 'vitest' +import NP from '../src/operation' + +describe('operation', () => { + // let originalWarn: any + + // beforeAll(() => { + // console.log('生命周期---BeforeAll--在本文件所有测试用例执行之前') + // originalWarn = global.console.warn + // global.console.warn = jest.fn() + // }) + + // beforeEach(() => { + // console.log('生命周期---BeforeEach--在每一个测试用例执行之前') + // }) + + // afterEach(() => { + // console.log('生命周期---AfterEach--在每一个测试用例执行之后') + // }) + + // afterAll(() => { + // console.log('生命周期---AfterAll--在测试用例执行结束之后') + // global.console.warn = originalWarn + // }) + + test('NP.strip 消除 舍入 运算的误差', () => { + expect(NP.strip(0.09999999999999998) === 0.1).toBeTruthy() + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + expect(NP.strip(1.0000000000000001) === 1).toBeTruthy() + expect(NP.strip('0.09999999999999998') === 0.1).toBeTruthy() + expect(NP.strip('1.0000000000000001') === 1).toBeTruthy() + }) + + test('NP.digitLength 数字长度运算', () => { + expect(NP.digitLength(123.4567890123) === 10).toBeTruthy() + + expect(NP.digitLength(1.23e-5) === 7).toBeTruthy() + expect(NP.digitLength(1.23e-5) === 7).toBeTruthy() + expect(NP.digitLength(1.233467e-5) === 11).toBeTruthy() + expect(NP.digitLength(123.45e-5) === 7).toBeTruthy() + expect(NP.digitLength(1.23e-10) === 12).toBeTruthy() + expect(NP.digitLength(1.23e1) === 1).toBeTruthy() + expect(NP.digitLength(1e20) === 0).toBeTruthy() + expect(NP.digitLength(1.12345e20) === 0).toBeTruthy() + expect(NP.digitLength(1.123e30) === 0).toBeTruthy() + expect(NP.digitLength(1.123e-100) === 103).toBeTruthy() + expect(NP.digitLength('1.23e-5') === 7).toBeTruthy() + expect(NP.digitLength('1.23E-5') === 7).toBeTruthy() + expect(NP.digitLength('1.233467e-5') === 11).toBeTruthy() + expect(NP.digitLength('123.45e-5') === 7).toBeTruthy() + expect(NP.digitLength('1.23e-10') === 12).toBeTruthy() + expect(NP.digitLength('1.23e1') === 1).toBeTruthy() + expect(NP.digitLength('1e20') === 0).toBeTruthy() + expect(NP.digitLength('1.12345e20') === 0).toBeTruthy() + expect(NP.digitLength('1.123e30') === 0).toBeTruthy() + expect(NP.digitLength('1.123e-100') === 103).toBeTruthy() + }) + + test('NP.float2Int can change float to fixed', () => { + expect(NP.float2Int(1e-1) === 1).toBeTruthy() + expect(NP.float2Int(1e-6) === 1).toBeTruthy() + expect(NP.float2Int(1e-7) === 1).toBeTruthy() + expect(NP.float2Int(1e-13) === 1).toBeTruthy() + expect(NP.float2Int(1.123e30) === 1.123e30).toBeTruthy() + expect(NP.float2Int(1.6e-30) === 16).toBeTruthy() + expect(NP.float2Int(1.234567e-13) === 1234567).toBeTruthy() + expect(NP.float2Int(1.2345678912345e10) === 12345678912345).toBeTruthy() + expect(NP.float2Int(0.000000123456) === 123456).toBeTruthy() + expect(NP.float2Int(1.23456e-7) === 123456).toBeTruthy() + expect(NP.float2Int('1e-1') === 1).toBeTruthy() + expect(NP.float2Int('1e-6') === 1).toBeTruthy() + expect(NP.float2Int('1e-7') === 1).toBeTruthy() + expect(NP.float2Int('1e-13') === 1).toBeTruthy() + expect(NP.float2Int('1.123e30') === 1.123e30).toBeTruthy() + expect(NP.float2Int('1.6e-30') === 16).toBeTruthy() + expect(NP.float2Int('1.234567e-13') === 1234567).toBeTruthy() + expect( + NP.float2Int('1.2345678912345e10') === 12345678912345 + ).toBeTruthy() + expect(NP.float2Int('0.000000123456') === 123456).toBeTruthy() + expect(NP.float2Int('1.23456e-7') === 123456).toBeTruthy() + }) + + test('NP.plus 精确加法运算', () => { + expect(NP.plus(0.1, 0.2) === 0.3).toBeTruthy() + expect(NP.plus(2.3, 2.4) === 4.7).toBeTruthy() + expect(NP.plus(-1.6, -1) === -2.6).toBeTruthy() + expect(NP.plus(-2.0, 63) === 61).toBeTruthy() + expect(NP.plus(-3, 7) === 4).toBeTruthy() + expect(NP.plus(-221, 38) === -183).toBeTruthy() + expect(NP.plus(-1, 0) === -1).toBeTruthy() + expect(NP.plus(2.018, 0.001) === 2.019).toBeTruthy() + expect(NP.plus(1.3224e10, 1.3224e3) === 13224001322.4).toBeTruthy() + expect(NP.plus(1.6e-30, 1.6e-30) === 3.2e-30).toBeTruthy() + expect(NP.plus('0.1', '0.2') === 0.3).toBeTruthy() + expect(NP.plus('2.3', '2.4') === 4.7).toBeTruthy() + expect(NP.plus('-1.6', '-1') === -2.6).toBeTruthy() + expect(NP.plus('-2.0', '63') === 61).toBeTruthy() + expect(NP.plus('-3', '7') === 4).toBeTruthy() + expect(NP.plus('-221', '38') === -183).toBeTruthy() + expect(NP.plus('-1', '0') === -1).toBeTruthy() + expect(NP.plus('-1', '0', '2', '3', 4) === 8).toBeTruthy() + expect(NP.plus('2.018', '0.001') === 2.019).toBeTruthy() + expect(NP.plus('1.3224e10', '1.3224e3') === 13224001322.4).toBeTruthy() + expect(NP.plus('1.6e-30', '1.6e-30') === 3.2e-30).toBeTruthy() + + expect(NP.plus(0.1, 0.2, 0.3) === 0.6).toBeTruthy() + expect(NP.plus('0.1', '0.2', '0.3') === 0.6).toBeTruthy() + + expect(NP.plus(...new Array(500).fill(1)) === 500).toBeTruthy() + }) + + test('NP.minus 精确减法运算', () => { + expect(NP.minus(0.07, 0.01) === 0.06).toBeTruthy() + expect(NP.minus(0.7, 0.1) === 0.6).toBeTruthy() + expect(NP.minus(1.0, 0.9) === 0.1).toBeTruthy() + expect(NP.minus(1, 0) === 1).toBeTruthy() + expect(NP.minus(1, -0) === 1).toBeTruthy() + expect(NP.minus(-1, 0) === -1).toBeTruthy() + expect(NP.minus(-1, -0) === -1).toBeTruthy() + expect(NP.minus(1, 22) === -21).toBeTruthy() + expect( + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + NP.minus(8893568.397103781249, -7.2967405955) === + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + 8893575.693844376749 + ).toBeTruthy() + expect(NP.minus(105468873, 0) === 105468873).toBeTruthy() + expect(NP.minus('0.07', '0.01') === 0.06).toBeTruthy() + expect(NP.minus('0.7', '0.1') === 0.6).toBeTruthy() + expect(NP.minus('1.0', '0.9') === 0.1).toBeTruthy() + expect(NP.minus('1', '0') === 1).toBeTruthy() + expect(NP.minus('1', '-0') === 1).toBeTruthy() + expect(NP.minus('-1', '0') === -1).toBeTruthy() + expect(NP.minus('-1', '-0') === -1).toBeTruthy() + expect(NP.minus('1', '22') === -21).toBeTruthy() + expect( + NP.minus('8893568.397103781249', '-7.29674059550') === + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + 8893575.693844376749 + ).toBeTruthy() + expect(NP.minus('105468873', '0') === 105468873).toBeTruthy() + + expect(NP.minus(1.23e5, 10) === 122990).toBeTruthy() + expect(NP.minus(1.23e-5, 1.0023) === -1.0022877).toBeTruthy() + expect(NP.minus(1.3224e10, 21) === 13223999979).toBeTruthy() + expect(NP.minus(1.3224e10, 1.3224e3) === 13223998677.6).toBeTruthy() + expect(NP.minus(1.7e-30, 0.1e-30) === 1.6e-30).toBeTruthy() + expect(NP.minus('1.23e5', '10') === 122990).toBeTruthy() + expect(NP.minus('1.23e-5', '1.0023') === -1.0022877).toBeTruthy() + expect(NP.minus('1.3224e10', '21') === 13223999979).toBeTruthy() + expect(NP.minus('1.3224e10', '1.3224e3') === 13223998677.6).toBeTruthy() + expect(NP.minus('1.7e-30', '0.1e-30') === 1.6e-30).toBeTruthy() + + expect(NP.minus(6, 3, 2) === 1).toBeTruthy() + expect(NP.minus(6, 3, 2, 1, 2, 3) === -5).toBeTruthy() + expect(NP.minus('6', '3', '2') === 1).toBeTruthy() + expect(NP.minus('6', '3', '2', '1', '2', '3') === -5).toBeTruthy() + + expect(NP.minus(500, ...new Array(500).fill(1)) === 0).toBeTruthy() + }) + + test('NP.times 精确乘法运算', () => { + expect(NP.times(0.07, 100) === 7).toBeTruthy() + expect(NP.times(0.7, 0.1) === 0.07).toBeTruthy() + expect(NP.times(3, 0.3) === 0.9).toBeTruthy() + expect(NP.times(118762317358.75, 1e-8) === 1187.6231735875).toBeTruthy() + expect(NP.times(0.362, 100) === 36.2).toBeTruthy() + expect(NP.times(1.1, 1.1) === 1.21).toBeTruthy() + expect(NP.times(2.018, 1000) === 2018).toBeTruthy() + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + expect(NP.times(5.2, -3.8461538461538462) === -20).toBeTruthy() + expect(NP.times(1.22, -1.639344262295082) === -2).toBeTruthy() + expect(NP.times(2.5, -0.92) === -2.3).toBeTruthy() + expect(NP.times(-2.2, 0.6363636363636364) === -1.4).toBeTruthy() + expect(NP.times('0.07', '100') === 7).toBeTruthy() + expect(NP.times('0.7', '0.1') === 0.07).toBeTruthy() + expect(NP.times('3', '0.3') === 0.9).toBeTruthy() + expect( + NP.times('118762317358.75', '1e-8') === 1187.6231735875 + ).toBeTruthy() + expect(NP.times('0.362', '100') === 36.2).toBeTruthy() + expect(NP.times('1.1', '1.1') === 1.21).toBeTruthy() + expect(NP.times('2.018', '1000') === 2018).toBeTruthy() + expect(NP.times('5.2', '-3.8461538461538462') === -20).toBeTruthy() + expect(NP.times('1.22', '-1.639344262295082') === -2).toBeTruthy() + expect(NP.times('2.5', '-0.92') === -2.3).toBeTruthy() + expect(NP.times('-2.2', '0.6363636363636364') === -1.4).toBeTruthy() + // expect(NP.times(-3, 2.3333333333333335) === 7).toBeTruthy() + // expect(NP.times(-0.076, -92.10526315789471) === 7).toBeTruthy() + expect(NP.times(8.0, -0.3625) === -2.9).toBeTruthy() + expect(NP.times(6.6, 0.30303030303030304) === 2).toBeTruthy() + expect(NP.times(10.0, -0.8) === -8).toBeTruthy() + expect(NP.times(-1.1, -7.272727272727273) === 8).toBeTruthy() + expect(NP.times('8.0', '-0.3625') === -2.9).toBeTruthy() + expect(NP.times('6.6', '0.30303030303030304') === 2).toBeTruthy() + expect(NP.times('10.0', '-0.8') === -8).toBeTruthy() + expect(NP.times('-1.1', '-7.272727272727273') === 8).toBeTruthy() + + expect(NP.times(-1.23e4, 20) === -246000).toBeTruthy() + expect(NP.times(1.7e-30, 1.5e20) === 2.55e-10).toBeTruthy() + expect(NP.times('-1.23e4', '20') === -246000).toBeTruthy() + expect(NP.times('1.7e-30', '1.5e20') === 2.55e-10).toBeTruthy() + + expect(NP.times(2, 2, 3) === 12).toBeTruthy() + expect(NP.times(2, 2, 3, 0.1) === 1.2).toBeTruthy() + expect(NP.times('2', '2', '3') === 12).toBeTruthy() + expect(NP.times('2', '2', '3', '0.1') === 1.2).toBeTruthy() + + expect( + NP.times(0.000000123456, 0.000000123456) === 1.5241383936e-14 + ).toBeTruthy() + expect( + NP.times(1.23456e-7, 1.23456e-7) === 1.5241383936e-14 + ).toBeTruthy() + expect( + NP.times('0.000000123456', '0.000000123456') === 1.5241383936e-14 + ).toBeTruthy() + expect( + NP.times('1.23456e-7', '1.23456e-7') === 1.5241383936e-14 + ).toBeTruthy() + + expect(NP.times(...new Array(500).fill(1)) === 1).toBeTruthy() + }) + + test('NP.divide 精确除法运算', () => { + expect(NP.divide(1.21, 1.1) === 1.1).toBeTruthy() + expect(NP.divide(4750.49269435, 4) === 1187.6231735875).toBeTruthy() + expect(NP.divide(0.9, 3) === 0.3).toBeTruthy() + expect(NP.divide(36.2, 0.362) === 100).toBeTruthy() + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + expect(NP.divide(-20, 5.2) === -3.8461538461538462).toBeTruthy() + expect(NP.divide(-2, 1.22) === -1.639344262295082).toBeTruthy() + expect(NP.divide(-2.3, 2.5) === -0.92).toBeTruthy() + expect(NP.divide(-1.4, -2.2) === 0.6363636363636364).toBeTruthy() + expect(NP.divide(7, -3) === -2.3333333333333335).toBeTruthy() + expect(NP.divide(7, -0.076) === -92.10526315789471).toBeTruthy() + expect(NP.divide(-2.9, 8.0) === -0.3625).toBeTruthy() + expect(NP.divide(2, 6.6) === 0.30303030303030304).toBeTruthy() + expect(NP.divide(-8, 10.0) === -0.8).toBeTruthy() + expect(NP.divide(8, -1.1) === -7.272727272727273).toBeTruthy() + expect(NP.divide('1.21', '1.1') === 1.1).toBeTruthy() + expect(NP.divide('4750.49269435', '4') === 1187.6231735875).toBeTruthy() + expect(NP.divide('0.9', '3') === 0.3).toBeTruthy() + expect(NP.divide('36.2', '0.362') === 100).toBeTruthy() + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + expect(NP.divide('-20', '5.2') === -3.8461538461538462).toBeTruthy() + expect(NP.divide('-2', '1.22') === -1.639344262295082).toBeTruthy() + expect(NP.divide('-2.3', '2.5') === -0.92).toBeTruthy() + expect(NP.divide('-1.4', '-2.2') === 0.6363636363636364).toBeTruthy() + expect(NP.divide('7', '-3') === -2.3333333333333335).toBeTruthy() + expect(NP.divide('7', '-0.076') === -92.10526315789471).toBeTruthy() + expect(NP.divide('-2.9', '8.0') === -0.3625).toBeTruthy() + expect(NP.divide('2', '6.6') === 0.30303030303030304).toBeTruthy() + expect(NP.divide('-8', '10.0') === -0.8).toBeTruthy() + expect(NP.divide('8', '-1.1') === -7.272727272727273).toBeTruthy() + + expect(NP.divide(-1.23e4, 20) === -615).toBeTruthy() + expect(NP.divide(2.55e-10, 1.7e-30) === 1.5e20).toBeTruthy() + expect(NP.divide('-1.23e4', '20') === -615).toBeTruthy() + expect(NP.divide('2.55e-10', '1.7e-30') === 1.5e20).toBeTruthy() + + expect(NP.divide(12, 3, 2) === 2).toBeTruthy() + expect(NP.divide(33.3333, 100) === 0.333333).toBeTruthy() + expect(NP.divide(83.42894732749, 100) === 0.8342894732749).toBeTruthy() + expect(NP.divide(1, 3) === 0.3333333333333333).toBeTruthy() + expect(NP.divide('12', '3', '2') === 2).toBeTruthy() + expect(NP.divide('33.3333', '100') === 0.333333).toBeTruthy() + expect( + NP.divide('83.42894732749', '100') === 0.8342894732749 + ).toBeTruthy() + expect(NP.divide('1', '3') === 0.3333333333333333).toBeTruthy() + + expect(NP.divide(...new Array(500).fill(1)) === 1).toBeTruthy() + expect(NP.divide(1024, 4, 8, 2) === 16).toBeTruthy() + }) + + test('NP.accurateRound 精确四舍五入运算', () => { + expect(NP.accurateRound(0, 1) === 0).toBeTruthy() + expect(NP.accurateRound(0, 0) === 0).toBeTruthy() + expect(NP.accurateRound(0.7875, 3) === 0.788).toBeTruthy() + expect(NP.accurateRound(0.105, 2) === 0.11).toBeTruthy() + expect(NP.accurateRound(1, 1) === 1).toBeTruthy() + expect(NP.accurateRound(0.1049999999, 2) === 0.1).toBeTruthy() + expect(NP.accurateRound(0.105, 1) === 0.1).toBeTruthy() + expect(NP.accurateRound(1.335, 2) === 1.34).toBeTruthy() + expect(NP.accurateRound(1.35, 1) === 1.4).toBeTruthy() + expect(NP.accurateRound(12345.105, 2) === 12345.11).toBeTruthy() + expect(NP.accurateRound(0.0005, 2) === 0).toBeTruthy() + expect(NP.accurateRound(0.0005, 3) === 0.001).toBeTruthy() + expect(NP.accurateRound('0', 1) === 0).toBeTruthy() + expect(NP.accurateRound('0', 0) === 0).toBeTruthy() + expect(NP.accurateRound('0.7875', 3) === 0.788).toBeTruthy() + expect(NP.accurateRound('0.105', 2) === 0.11).toBeTruthy() + expect(NP.accurateRound('1', 1) === 1).toBeTruthy() + expect(NP.accurateRound('0.1049999999', 2) === 0.1).toBeTruthy() + expect(NP.accurateRound('0.105', 1) === 0.1).toBeTruthy() + expect(NP.accurateRound('1.335', 2) === 1.34).toBeTruthy() + expect(NP.accurateRound('1.35', 1) === 1.4).toBeTruthy() + expect(NP.accurateRound('12345.105', 2) === 12345.11).toBeTruthy() + expect(NP.accurateRound('0.0005', 2) === 0).toBeTruthy() + expect(NP.accurateRound('0.0005', 3) === 0.001).toBeTruthy() + + expect(NP.accurateRound(1.2345e3, 3) === 1234.5).toBeTruthy() + expect(NP.accurateRound(1.2344e3, 3) === 1234.4).toBeTruthy() + expect(NP.accurateRound(1e3, 1) === 1000).toBeTruthy() + expect(NP.accurateRound('1.2345e3', 3) === 1234.5).toBeTruthy() + expect(NP.accurateRound('1.2344e3', 3) === 1234.4).toBeTruthy() + expect(NP.accurateRound('1e3', 1) === 1000).toBeTruthy() + + expect(NP.accurateRound('-0.125', 2) === -0.13).toBeTruthy() + expect(NP.accurateRound('-0.001', 2) === 0.0).toBeTruthy() + expect(NP.accurateRound('-0.005', 2) === -0.01).toBeTruthy() + expect(NP.accurateRound('0.125', 2) === 0.13).toBeTruthy() + expect(NP.accurateRound('0.001', 2) === 0.0).toBeTruthy() + expect(NP.accurateRound('0.005', 2) === 0.01).toBeTruthy() + expect(NP.accurateRound(-0.125, 2) === -0.13).toBeTruthy() + expect(NP.accurateRound(-0.001, 2) === 0.0).toBeTruthy() + expect(NP.accurateRound(-0.005, 2) === -0.01).toBeTruthy() + expect(NP.accurateRound(0.125, 2) === 0.13).toBeTruthy() + expect(NP.accurateRound(0.001, 2) === 0.0).toBeTruthy() + expect(NP.accurateRound(0.005, 2) === 0.01).toBeTruthy() + + expect(NP.accurateRound(123.345, 2) === 123.35).toBeTruthy() + }) +}) diff --git a/packages/number/package.json b/packages/number/package.json new file mode 100644 index 0000000..1f9aead --- /dev/null +++ b/packages/number/package.json @@ -0,0 +1,39 @@ +{ + "name": "@jucttr/number", + "version": "0.3.0", + "description": "一个测试发布流程的包", + "main": "./dist/cjs/index.js", + "module": "./dist/es/index.js", + "exports": { + ".": { + "import": "./dist/es/index.js", + "types": "./types/index.d.ts", + "default": "./dist/cjs/index.js" + }, + "./package.json": "./package.json" + }, + "types": "./types/index.d.ts", + "files": [ + "dist", + "!dist/**/*.map", + "lib", + "types", + "README.md" + ], + "engines": { + "node": ">=14.0.0" + }, + "scripts": { + "build": "rimraf dist && vite build", + "test": "pnpm run test:types", + "test:types": "tsc --noEmit", + "prepublish": "pnpm build" + }, + "keywords": [], + "author": "jucttr", + "license": "ISC", + "publishConfig": { + "access": "public" + }, + "gitHead": "61f1d26b0e169a0bce7eed45329d913fce782fcb" +} diff --git a/packages/number/src/addComma.ts b/packages/number/src/addComma.ts new file mode 100644 index 0000000..67630b4 --- /dev/null +++ b/packages/number/src/addComma.ts @@ -0,0 +1,19 @@ +/** + * 千分位分割数字 + * @param {Number} num + * @returns {String} + * + * @example + * addComma(123456789) // '123,456,789' + */ +function addComma(num: number): string { + return `${num}`.replace(/(^|\s|-)\d+/g, m => + m.replace(/(?=(?!\b)(\d{3})+$)/g, ',') + ) +} + +export { addComma } + +export default { + addComma +} diff --git a/packages/number/src/index.ts b/packages/number/src/index.ts new file mode 100644 index 0000000..b757f1d --- /dev/null +++ b/packages/number/src/index.ts @@ -0,0 +1,6 @@ +/** + * @module @base/number + * @author jucttr + */ + +export * from './operation' diff --git a/packages/number/src/operation.ts b/packages/number/src/operation.ts new file mode 100644 index 0000000..3bdb1d8 --- /dev/null +++ b/packages/number/src/operation.ts @@ -0,0 +1,232 @@ +/** + * @module @base/number + * @author jucttr + * @reference https://github.com/nefe/number-precision + * @desc 解决浮动运算问题,避免小数点后产生多位数和计算精度丢失,使得 javascript 能够精确执行加、减、乘、除运算。 + * + * 问题示例:2.3 + 2.4 = 4.699999999999999,1.0 - 0.9 = 0.09999999999999998 + */ + +type NumberType = number | string + +let boundaryCheckingState = true + +/** + * 如果输入的数字超出范围,则打印警告。 + * + * @param {Number} num + */ +function checkBoundary(num: number) { + if (boundaryCheckingState) { + if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) { + console.warn(`${num} 转换成整数时超出边界,结果可能不准确`) + } + } +} + +/** + * 是否检查数字范围,默认开启 + * + * PS:如果你无需数字边界检查,想忽略“ XXX 转换成整数时超出边界,结果可能不准确” 警告,可以在应用程序的开头使用它来关闭边界检查。 + * @param {Boolean} flag + */ +function enableBoundaryChecking(flag = true) { + boundaryCheckingState = flag +} + +/** + * 基础操作函数,支持rest参数 + * + * 利用柯里化思想,编写一个高阶函数,用于生成精确的加减乘除运算函数。 + * @param {Function} operation + */ +function createOperation( + operation: (n1: NumberType, n2: NumberType) => number +): (...nums: NumberType[]) => number { + return (...nums: NumberType[]) => { + const [first, ...others] = nums + return others.reduce( + (prev, next) => operation(prev, next), + first + ) as number + } +} + +/** + * 将输入的数字更正为指定的有效数字 + * + * @param {NumberType} num + * @param {Number} precision 指定有效位数的整数 + * + * @example + * import { strip } from '@shein/common-function' + * + * strip(0.09999999999999998) === 0.1 // true + */ +function strip(num: NumberType, precision = 15): number { + return +parseFloat(Number(num).toPrecision(precision)) +} + +/** + * 返回一个数字小数点后的位数长度 + * + * @param {NumberType} num + * @example + * import { digitLength } from '@shein/common-function' + * + * digitLength(0.3) // 1 + * digitLength(0.69) // 2 + */ +function digitLength(num: NumberType): number { + // 对于科学计数法的处理 + const eSplit = num.toString().split(/[eE]/) + const len = (eSplit[0].split('.')[1] || '').length - Number(eSplit[1] || 0) + return len > 0 ? len : 0 +} + +/** + * 将输入的数字转换为整数,支持科学记数法 + * 如果是小数,则该数字将按比例放大 + * + * @param {NumberType} num + * @example + * import { float2Int } from '@shein/common-function' + * + * float2Int(0.3) // 3 + * float2Int(0.69) // 69 + * + */ +function float2Int(num: NumberType): number { + if (num.toString().indexOf('e') === -1) { + return Number(num.toString().replace('.', '')) + } + const dLen = digitLength(num) + return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num) +} + +/** + * 精确加法 + * + * @param nums + * @returns {Number} 返回处理后的数字 + * @example + * import { plus } from '@shein/common-function' + * + * plus(0.3, 0.1) // 0.4 + * plus(0.69, 0.1) // 0.79 + */ +const plus = createOperation((num1, num2) => { + // 取最大的小数位 + const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2))) + // 把小数都转为整数然后再计算 + return (times(num1, baseNum) + times(num2, baseNum)) / baseNum +}) + +/** + * 精确减法 + * + * @param nums + * @returns {Number} 返回处理后的数字 + * @example + * import { minus } from '@shein/common-function' + * + * minus(0.3, 0.1) // 0.2 + * minus(0.69, 0.1) // 0.59 + */ +const minus = createOperation((num1, num2) => { + const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2))) + return (times(num1, baseNum) - times(num2, baseNum)) / baseNum +}) + +/** + * 精确乘法 + * + * @param nums 要相乘的数字 + * @returns {Number} 返回处理后的数字 + * @example + * import { times } from '@shein/common-function' + * + * times(0.3, 0.1) // 0.03 + * times(0.69, 0.1) // 0.069 + */ +const times = createOperation((num1, num2) => { + const num1Changed = float2Int(num1) + const num2Changed = float2Int(num2) + const baseNum = digitLength(num1) + digitLength(num2) + const leftValue = num1Changed * num2Changed + + checkBoundary(leftValue) + + return leftValue / Math.pow(10, baseNum) +}) + +/** + * 精确除法 + * + * @param nums + * @returns {number} 返回处理后的数字 + * @example + * import { divide } from '@shein/common-function' + * + * divide(0.3, 0.1) // 3 + * divide(0.69, 0.1) // 6.9 + */ +const divide = createOperation((num1, num2) => { + const num1Changed = float2Int(num1) + const num2Changed = float2Int(num2) + + checkBoundary(num1Changed) + checkBoundary(num2Changed) + + return times( + num1Changed / num2Changed, + strip(Math.pow(10, digitLength(num2) - digitLength(num1))) + ) +}) + +/** + * 精确四舍五入 + * + * @param {NumberType} num 输入待处理的数字 + * @param {number} decimal 指定小数位数的整数 + * @returns {number} 返回处理后的数字 + * @example + * import { accurateRound } from '@shein/common-function' + * + * accurateRound(0.105, 2) // 0.11 + * accurateRound(1.005, 2) // 1.01 + */ +function accurateRound(num: NumberType, decimal: number): number { + const base = Math.pow(10, decimal) + let result = divide(Math.round(Math.abs(times(num, base))), base) + + if (Number(num) < 0 && result !== 0) { + result = times(result, -1) + } + + return result +} + +export { + strip, + plus, + minus, + times, + divide, + accurateRound, + digitLength, + float2Int, + enableBoundaryChecking +} + +export default { + strip, + plus, + minus, + times, + divide, + accurateRound, + digitLength, + float2Int, + enableBoundaryChecking +} diff --git a/packages/number/tsconfig.json b/packages/number/tsconfig.json new file mode 100644 index 0000000..77d2cb2 --- /dev/null +++ b/packages/number/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"], + "compileOnSave": true +} diff --git a/packages/number/vite.config.ts b/packages/number/vite.config.ts new file mode 100644 index 0000000..c2d395e --- /dev/null +++ b/packages/number/vite.config.ts @@ -0,0 +1,9 @@ +import { readFileSync } from 'fs' + +import { createConfig } from '../../shared/vite.config.base' + +export default createConfig({ + pkg: JSON.parse( + readFileSync(new URL('./package.json', import.meta.url), 'utf8') + ) +}) diff --git a/scripts/create-new-package.js b/scripts/create-new-package.js new file mode 100644 index 0000000..1775b6c --- /dev/null +++ b/scripts/create-new-package.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +// const packageName = process.argv || 'jucttr-test' + +// console.log(process.argv) + +// TODO