├── .circleci └── config.yml ├── .editorconfig ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── node-ci.yml │ └── npm-publish.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .npmignore ├── .npmrc ├── .prettierignore ├── .vscode ├── debug-ts.js ├── launch.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh-CN.md ├── benchmark ├── benchmark.ts ├── calc │ ├── decimal-calc.ts │ ├── js-raw-calc.ts │ └── mathjs-calc.ts └── index.ts ├── eslint.config.cjs ├── jest.config.ts ├── package.json ├── src ├── __test__ │ ├── asmd-calc.spec.ts │ ├── calculation.spec.ts │ └── utils.spec.ts ├── index.ts └── lib │ ├── asmd-calc.class.ts │ ├── calculation.ts │ └── utils.ts ├── tsconfig.cjs.json ├── tsconfig.eslint.json ├── tsconfig.json └── tsconfig.module.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # https://circleci.com/docs/2.0/language-javascript/ 2 | version: 2 3 | jobs: 4 | 'node-10': 5 | docker: 6 | - image: circleci/node:10 7 | working_directory: ~/asmd-calc 8 | steps: 9 | - checkout 10 | # Download and cache dependencies 11 | - restore_cache: 12 | keys: 13 | - v1-dependencies-{{ checksum "package.json" }} 14 | # fallback to using the latest cache if no exact match is found 15 | - v1-dependencies- 16 | - run: npm install 17 | - save_cache: 18 | paths: 19 | - node_modules 20 | key: v1-dependencies-{{ checksum "package.json" }} 21 | - run: npm test 22 | - run: npm run cov:send 23 | - run: npm run cov:check 24 | 'node-latest': 25 | docker: 26 | - image: circleci/node:latest 27 | working_directory: ~/asmd-calc 28 | steps: 29 | - checkout 30 | - restore_cache: 31 | keys: 32 | - v1-dependencies-{{ checksum "package.json" }} 33 | - v1-dependencies- 34 | - run: npm install 35 | - save_cache: 36 | paths: 37 | - node_modules 38 | key: v1-dependencies-{{ checksum "package.json" }} 39 | - run: npm test 40 | - run: npm run cov:send 41 | - run: npm run cov:check 42 | 43 | workflows: 44 | version: 2 45 | build: 46 | jobs: 47 | - 'node-10' 48 | - 'node-latest' 49 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | max_line_length = 140 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | max_line_length = 0 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.ts text eol=lf 3 | *.bat text eol=crlf 4 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Example Contributing Guidelines 2 | 3 | This is an example of GitHub's contributing guidelines file. Check out GitHub's [CONTRIBUTING.md help center article](https://help.github.com/articles/setting-guidelines-for-repository-contributors/) for more information. 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * **I'm submitting a ...** 2 | [ ] bug report 3 | [ ] feature request 4 | [ ] question about the decisions made in the repository 5 | [ ] question about how to use this project 6 | 7 | * **Summary** 8 | 9 | 10 | 11 | * **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.) 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) 2 | 3 | 4 | 5 | * **What is the current behavior?** (You can also link to an open issue here) 6 | 7 | 8 | 9 | * **What is the new behavior (if this is a feature change)?** 10 | 11 | 12 | 13 | * **Other information**: 14 | -------------------------------------------------------------------------------- /.github/workflows/node-ci.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests' 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | paths-ignore: 7 | - README.md 8 | - CONTRIBUTING.md 9 | pull_request: 10 | branches: 11 | - '**' 12 | jobs: 13 | test: 14 | runs-on: ${{ matrix.os }} 15 | continue-on-error: ${{ matrix.os == 'windows-latest' }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - macos-latest 22 | - windows-latest 23 | node-version: 24 | - 20 25 | include: 26 | - node-version: 18 27 | os: ubuntu-latest 28 | name: Node ${{ matrix.node-version }} on ${{ matrix.os }} 29 | steps: 30 | - uses: actions/checkout@v4 31 | with: 32 | submodules: 'recursive' 33 | - uses: pnpm/action-setup@v4 34 | with: 35 | version: 9 36 | - uses: actions/setup-node@v4 37 | with: 38 | node-version: ${{ matrix.node-version }} 39 | # cache: 'pnpm' 40 | - run: pnpm install 41 | - run: pnpm test 42 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 2 | 3 | name: Publish Package to npmjs 4 | 5 | on: 6 | push: 7 | tags: 8 | - "v*.*.*" 9 | # release: 10 | # types: [created] 11 | 12 | jobs: 13 | npm-publish: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | concurrency: 18 | group: ${{ github.workflow }}-${{ github.ref }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | with: 23 | submodules: 'recursive' 24 | 25 | - name: Install Node.js 26 | uses: actions/setup-node@v4 27 | with: 28 | node-version: 22 29 | registry-url: https://registry.npmjs.com 30 | 31 | - uses: pnpm/action-setup@v4 32 | name: Install pnpm 33 | id: pnpm-install 34 | with: 35 | run_install: false 36 | 37 | - name: Get pnpm store directory 38 | id: pnpm-cache 39 | shell: bash 40 | run: | 41 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT 42 | 43 | - uses: actions/cache@v3 44 | name: Setup pnpm cache 45 | with: 46 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 47 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 48 | restore-keys: | 49 | ${{ runner.os }}-pnpm-store- 50 | 51 | - name: Install dependencies 52 | run: pnpm install --no-frozen-lockfile --ignore-scripts 53 | 54 | - name: Build and npm-publish 55 | run: npm run release 56 | 57 | - run: npm publish 58 | env: 59 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 60 | 61 | - name: GitHub Pages action 62 | uses: peaceiris/actions-gh-pages@v3 63 | with: 64 | github_token: ${{ secrets.GITHUB_TOKEN }} 65 | publish_dir: ./docs 66 | 67 | - name: Github Release 68 | uses: softprops/action-gh-release@v1 69 | if: startsWith(github.ref, 'refs/tags/') 70 | with: 71 | draft: false 72 | prerelease: false 73 | # tag_name: ${{ github.ref }} 74 | # name: Release ${{ github.ref }} 75 | # body: TODO New Release. 76 | # files: | 77 | # ${{ secrets.ReleaseZipName }}.zip 78 | # LICENSE 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /build/ 3 | /docs/ 4 | /test/ 5 | /coverage/ 6 | /cache/ 7 | /tmp/ 8 | src/**.js 9 | .idea/* 10 | 11 | coverage 12 | .nyc_output 13 | *.log 14 | 15 | package-lock.json 16 | yarn.lock 17 | .grs.config.js 18 | pnpm-lock.yaml 19 | .codegeexcount 20 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | # npm run pipeline:commit-msg -- --edit $1 2 | pnpm flh --commitlint 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm exec flh --eslint --tscheck --only-changes # --fix 2 | pnpm exec flh --prettier --only-changes --fix 3 | # pnpm lint-staged 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # source 2 | src 3 | **/*.spec.* 4 | test 5 | test-cases 6 | scripts 7 | README_zh-CN.md 8 | CHANGELOG.md 9 | LICENSE 10 | benchmark 11 | 12 | # build dist 13 | build/docs 14 | coverage 15 | .nyc_output 16 | *.log 17 | npm-debug.log 18 | 19 | # manager 20 | package-lock.json 21 | yarn.lock 22 | yarn-error.log 23 | pnpm-lock.yaml 24 | .pnpm-debug.log 25 | 26 | # config 27 | .gitattributes 28 | publish.bat 29 | .github 30 | .vscode 31 | .husky 32 | .circleci 33 | tsconfig.* 34 | jest.config.* 35 | .travis.yml 36 | .gitlab-ci.yml 37 | .editorconfig 38 | .eslintrc.js 39 | .eslintrc.json 40 | .prettierignore 41 | .prettierrc 42 | .grs.config.js 43 | .flh.config.js 44 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # for pnpm config 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # package.json is formatted by package managers, so we ignore it here 2 | package.json -------------------------------------------------------------------------------- /.vscode/debug-ts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const meow = require('meow'); 3 | const path = require('path'); 4 | 5 | const tsFile = getTSFile(); 6 | const jsFile = TS2JS(tsFile); 7 | 8 | replaceCLIArg(tsFile, jsFile); 9 | 10 | // Ava debugger 11 | require('ava/profile'); 12 | 13 | /** 14 | * get ts file path from CLI args 15 | * 16 | * @return string path 17 | */ 18 | function getTSFile() { 19 | const cli = meow(); 20 | return cli.input[0]; 21 | } 22 | 23 | /** 24 | * get associated compiled js file path 25 | * 26 | * @param tsFile path 27 | * @return string path 28 | */ 29 | function TS2JS(tsFile) { 30 | const srcFolder = path.join(__dirname, '..', 'src'); 31 | const distFolder = path.join(__dirname, '..', 'build', 'main'); 32 | 33 | const tsPathObj = path.parse(tsFile); 34 | 35 | return path.format({ 36 | dir: tsPathObj.dir.replace(srcFolder, distFolder), 37 | ext: '.js', 38 | name: tsPathObj.name, 39 | root: tsPathObj.root 40 | }); 41 | } 42 | 43 | /** 44 | * replace a value in CLI args 45 | * 46 | * @param search value to search 47 | * @param replace value to replace 48 | * @return void 49 | */ 50 | function replaceCLIArg(search, replace) { 51 | process.argv[process.argv.indexOf(search)] = replace; 52 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug Project", 8 | // we test in `build` to make cleanup fast and easy 9 | "cwd": "${workspaceFolder}/build", 10 | // Replace this with your project root. If there are multiple, you can 11 | // automatically run the currently visible file with: "program": ${file}" 12 | "program": "${workspaceFolder}/src/cli/cli.ts", 13 | // "args": ["--no-install"], 14 | "outFiles": ["${workspaceFolder}/build/main/**/*.js"], 15 | "skipFiles": ["/**/*.js", "${workspaceFolder}/node_modules/**/*.js"], 16 | "preLaunchTask": "npm: build", 17 | "stopOnEntry": true, 18 | "smartStep": true, 19 | "runtimeArgs": ["--nolazy"], 20 | "env": { 21 | "TYPESCRIPT_STARTER_REPO_URL": "${workspaceFolder}" 22 | }, 23 | "console": "externalTerminal" 24 | }, 25 | { 26 | "type": "node", 27 | "request": "launch", 28 | "name": "Debug Spec", 29 | "program": "${workspaceRoot}/.vscode/debug-ts.js", 30 | "args": ["${file}"], 31 | "skipFiles": ["/**/*.js"], 32 | // Consider using `npm run watch` or `pnpm watch` for faster debugging 33 | // "preLaunchTask": "npm: build", 34 | // "smartStep": true, 35 | "runtimeArgs": ["--nolazy"] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | // "typescript.implementationsCodeLens.enabled": true 4 | // "typescript.referencesCodeLens.enabled": true 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.3.4](https://github.com/lzwme/asmd-calc/compare/v1.3.3...v1.3.4) (2025-04-02) 6 | 7 | ### [1.3.3](https://github.com/lzwme/asmd-calc/compare/v1.3.2...v1.3.3) (2023-07-20) 8 | 9 | ### Bug Fixes 10 | 11 | - 恢复 types 输出方式,修正按文件加载时 ts 类型丢失的问题 ([963a56b](https://github.com/lzwme/asmd-calc/commit/963a56bd3e77b6ff0f80c74873c38726ca4a3d90)) 12 | - 修复字符串类型数字的加法运算结果为字符串拼接的问题 ([ff52802](https://github.com/lzwme/asmd-calc/commit/ff528025e035d52026692d9dbc217fbfcb8b8fb4)) 13 | - fix for isDecimal ([850a21e](https://github.com/lzwme/asmd-calc/commit/850a21e95c156e43fe01f9230ccc7fd5523542de)) 14 | - isDecimal() should return a boolean value ([c50b6d9](https://github.com/lzwme/asmd-calc/commit/c50b6d994eadfd6200be9aeae17d3724a7577b93)) 15 | 16 | ### [1.3.2](https://github.com/lzwme/asmd-calc/compare/v1.3.1...v1.3.2) (2022-05-09) 17 | 18 | ### Bug Fixes 19 | 20 | - fix for husky install scripts ([e4da474](https://github.com/lzwme/asmd-calc/commit/e4da474d056342726eb5f2845b896e76a26ecdee)) 21 | 22 | ### [1.3.1](https://github.com/lzwme/asmd-calc/compare/v1.3.0...v1.3.1) (2022-05-05) 23 | 24 | ### Bug Fixes 25 | 26 | - fix for types dir name ([c8518d0](https://github.com/lzwme/asmd-calc/commit/c8518d08dd68b35f1ea68081f2461dddb67ea912)) 27 | 28 | ## [1.3.0](https://github.com/lzwme/asmd-calc/compare/v1.1.5...v1.3.0) (2022-05-03) 29 | 30 | ### Features 31 | 32 | - 重构部分逻辑实现,改善执行性能,并增加 benchmark 基准测试 ([c6c5918](https://github.com/lzwme/asmd-calc/commit/c6c591872c34b739616f55ccdbf08f169008c886)) 33 | 34 | ## [1.2.0](https://github.com/lzwme/asmd-calc/compare/v1.1.5...v1.2.0) (2022-03-09) 35 | 36 | ### Features 37 | 38 | - 重构部分逻辑实现,改善执行性能,并增加 benchmark 基准测试 ([c6c5918](https://github.com/lzwme/asmd-calc/commit/c6c591872c34b739616f55ccdbf08f169008c886)) 39 | 40 | ## 1.1.5 (2021-09-25) 41 | 42 | ### Bug Fixes 43 | 44 | - 修复 tofixed 在计算科学计算法表示的数据时异常([#6](https://github.com/lzwme/asmd-calc/issues/6)) ([834c156](https://github.com/lzwme/asmd-calc/commit/608a2a87193cb03fa310b3811f7caf46f73d8df8)) 45 | 46 | ### [1.1.2](https://github.com/lzwme/asmd-calc/compare/v1.1.1...v1.1.2) (2021-03-30) 47 | 48 | ### Bug Fixes 49 | 50 | - 修复 toFixed 方法小数结果为 0 时返回值错误的问题 ([9fafac6](https://github.com/lzwme/asmd-calc/commit/9fafac6e6b75981808c16220b307d9e99cc71bcd)) 51 | 52 | ### [1.1.1](https://github.com/lzwme/asmd-calc/compare/v1.1.0...v1.1.1) (2021-03-20) 53 | 54 | ### Bug Fixes 55 | 56 | - 修复 toFixed 方法对于 9.99 存在进位后无小数位的情况会报错的问题 ([98c1197](https://github.com/lzwme/asmd-calc/commit/98c11972daf0b03f5f133975d35a5be04b5af61c)) 57 | 58 | ## 1.1.0 (2021-03-15) 59 | 60 | ### Features 61 | 62 | - 新增 toFixed 方法,取代 Number.toFixed 在不同浏览器表现不一致的问题 ([0267ff9](https://github.com/lzwme/asmd-calc/commit/0267ff9d58fbd9cb0eacfbfea37f2fe87fb72a70)) 63 | 64 | ### Bug Fixes 65 | 66 | - 修正 div 方法的 bug(修正 Math.pow(-5) 导致的精度错误问题) ([942bcfe](https://github.com/lzwme/asmd-calc/commit/942bcfe10a64636bd028e67c611638e7ae29d5e5)) 67 | 68 | ### 1.0.6 (2020-11-28) 69 | 70 | ### Bug Fixes 71 | 72 | - 修正 div 方法的 bug(修正 Math.pow(-5) 导致的精度错误问题) ([8c168d8](https://github.com/lzwme/asmd-calc/commit/8c168d8c096502cfce5576c344e35a401f012711)) 73 | 74 | ### 1.0.4 (2019-12-31) 75 | 76 | ### Features 77 | 78 | - 增加AsmdCalc.value的get属性,可以通过读取value值得到当前的计算结果 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 gflizhiwen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![@lzwme/asmd-calc](https://nodei.co/npm/@lzwme/asmd-calc.png)][download-url] 2 | 3 | # @lzwme/asmd-calc 4 | 5 | [![build status](https://github.com/lzwme/asmd-calc/actions/workflows/node-ci.yml/badge.svg)](https://github.com/lzwme/asmd-calc/actions/workflows/node-ci.yml) 6 | [![NPM version][npm-badge]][npm-url] 7 | [![node version][node-badge]][node-url] 8 | [![npm download][download-badge]][download-url] 9 | [![GitHub issues][issues-badge]][issues-url] 10 | [![GitHub forks][forks-badge]][forks-url] 11 | [![GitHub stars][stars-badge]][stars-url] 12 | ![license MIT](https://img.shields.io/github/license/lzwme/asmd-calc) 13 | 14 | 15 | 16 | [简体中文](./README-zh-CN.md) 17 | 18 | A short and concise JavaScript library for the four fundamental operations of arithmetic, which supports the addition, subtraction, multiplication and division calculation of floating-point precision. 19 | 20 | ## Features 21 | 22 | - Short and sharp. It only includes the calculation of addition, subtraction, multiplication and division. The core code is less than 100 lines. There is no other dependency. 23 | - High execution performance. Only the four operations that can be accurately expressed by the front end are considered to realize simple logic and better execution performance than the open source library with rich and complex functions (see `Benchmark` for details). 24 | - Accurate and stable. The accumulation of many years of practical experience in the development of several financial trading systems covers various common calculation use case error scenarios. 25 | 26 | **Not Applicable** 27 | 28 | - For numerical processing involving very large numbers that front-end can not be accurately represented. 29 | - Complex mathematical scientific calculation. 30 | 31 | These libraries are recommended: 32 | 33 | - [math.js](https://mathjs.org/index.html) 34 | - [decimal.js](https://github.com/MikeMcl/decimal.js) 35 | - [calculatorjs](https://github.com/fzred/calculatorjs) 36 | - [bignumber.js](https://github.com/MikeMcl/bignumber.js) 37 | - [big.js/](https://github.com/MikeMcl/big.js/)。 38 | 39 | ## Install 40 | 41 | ```bash 42 | # npm 43 | npm i @lzwme/asmd-calc 44 | # yarn 45 | pnpm add @lzwme/asmd-calc 46 | # pnpm 47 | pnpm add @lzwme/asmd-calc 48 | ``` 49 | 50 | ## Useage 51 | 52 | ### 1. Calculation example using simple tool method 53 | 54 | #### es module 55 | 56 | ```js 57 | import { add } from '@lzwme/asmd-calc'; 58 | 59 | console.log(add(0.1, 0.2, 0.3)); 60 | // => 0.6 61 | ``` 62 | 63 | #### commonjs 64 | 65 | ```js 66 | const calc = require('@lzwme/asmd-calc'); 67 | 68 | console.log(calc.add(0.1, 0.2, 0.3)); 69 | // => 0.6 70 | ``` 71 | 72 | ### 2. Calculation example using chain operation 73 | 74 | #### es module 75 | 76 | ```js 77 | import { AsmdCalc } from '@lzwme/asmd-calc'; 78 | 79 | const a = new AsmdCalc(0); 80 | console.log(+a.add(0.1).add(0.2, 0.3)); 81 | // => 0.6 82 | 83 | const b = new AsmdCalc(0.3); 84 | b.add(0.1, 0.2).add(0.1).sub(0.1, 0.2).sub(0.1).div(0.3).mul(0.3); 85 | console.log(+b); 86 | // => 0.3 87 | console.log(b.value); 88 | // => 0.3 89 | ``` 90 | 91 | #### commonjs 92 | 93 | ```js 94 | const AsmdCalc = require('@lzwme/asmd-calc').AsmdCalc; 95 | 96 | const calc = new AsmdCalc(0); 97 | console.log(calc.add(0.1).add(0.2, 0.3)); 98 | // => 0.6 99 | ``` 100 | 101 | ## API 102 | 103 | - `add(...args);` - Addition 104 | - `sub(...args);` - Subtraction 105 | - `mul(...args);` - Multiplication 106 | - `div(...args);` - Division 107 | - `keepDotLength(value: number | string, precision: number, type: 'ceil' | 'round' | 'fround' | 'floor' | boolean = 'floor'): number | null;` - Keep N decimal places 108 | - `toFixed(value: number | string, precision: number, type: 'ceil' | 'round' | 'fround' | 'floor' | boolean = 'floor'): string;` - Similar to `Number.prototype.toFixed`, but fixed precision of the result 109 | - `getDecimalLen(num): number;` - Get the decimal length 110 | - `toNonExponential(num): string;` - Convert to string format of unscientific counting method 111 | 112 | ## Development and testing 113 | 114 | - Development 115 | 116 | ```bash 117 | pnpm install 118 | pnpm start 119 | ``` 120 | 121 | - Testing 122 | 123 | ```bash 124 | npm test 125 | npm run benchmark 126 | ``` 127 | 128 | - Build 129 | 130 | ```bash 131 | npm run build 132 | ``` 133 | 134 | ## Benchmark 135 | 136 | ```bash 137 | npm run benchmark 138 | ``` 139 | 140 | See:[Benchmark](https://github.com/lzwme/asmd-calc/blob/master/benchmark/index.ts) 141 | 142 | The following results are the time-consuming comparison of executing `10000 * N` times on the same machine: 143 | 144 | | type/timeConst | jsRaw | asmd-calc | mathjs | 145 | | -------------- | -------- | --------- | --------- | 146 | | add-10000 | 19.225ms | 169.535ms | 415.145ms | 147 | | sub-10000 | 16.269ms | 34.827ms | 171.263ms | 148 | | mul-10000 | 18.518ms | 51.625ms | 235.868ms | 149 | | div-10000 | 27.025ms | 79.504ms | 300.706ms | 150 | 151 | Pre execution of 1000000 times and then stats time-consuming of execution of `10000 * N` times: 152 | 153 | | type/timeConst | jsRaw | asmd-calc | mathjs | 154 | | -------------- | -------- | --------- | --------- | 155 | | add-10000 | 7.768ms | 155.836ms | 362.819ms | 156 | | sub-10000 | 8.339ms | 25.147ms | 155.611ms | 157 | | mul-10000 | 9.995ms | 35.685ms | 224.357ms | 158 | | div-10000 | 15.666ms | 77.407ms | 280.322ms | 159 | 160 | ### Benchmark Details 161 | 162 | `random-10000times`: 163 | 164 | | type/timeConst | [jsRawCalc] | [asmdCalc] | [decimal] | [mathjs] | 165 | | --- | --- | --- | --- | --- | 166 | | add | 5.01ms | 59.3607ms | 95.3418ms | 144.9646ms | 167 | | sub | 5.0866ms | 9.3846ms | 33.8741ms | 62.0785ms | 168 | | mul | 5.4093ms | 14.9623ms | 54.6482ms | 94.0914ms | 169 | | div | 7.1796ms | 26.7458ms | 72.871ms | 117.2498ms | 170 | 171 | `decimal-10000times`: 172 | 173 | | type/timeConst | [jsRawCalc] | [asmdCalc] | [decimal] | [mathjs] | 174 | | --- | --- | --- | --- | --- | 175 | | add | 0.9363ms | 5.597ms | 12.3949ms | 18.1254ms | 176 | | sub | 0.7557ms | 7.9353ms | 12.6472ms | 18.1084ms | 177 | | mul | 0.4496ms | 7.2964ms | 14.6238ms | 20.0657ms | 178 | | div | 0.4945ms | 6.0127ms | 15.5085ms | 20.1288ms | 179 | 180 | `integer-10000times`: 181 | 182 | | type/timeConst | [jsRawCalc] | [asmdCalc] | [decimal] | [mathjs] | 183 | | --- | --- | --- | --- | --- | 184 | | add | 0.8038ms | 0.5012ms | 6.1372ms | 13.3008ms | 185 | | sub | 0.4676ms | 0.7482ms | 6.4895ms | 16.1568ms | 186 | | mul | 0.3157ms | 0.5464ms | 7.799ms | 15.9254ms | 187 | | div | 0.614ms | 0.4786ms | 9.8204ms | 17.2268ms | 188 | 189 | ## References 190 | 191 | - [754-2008 – IEEE Standard for Floating-Point Arithmetic](https://ieeexplore.ieee.org/document/4610935) 192 | - [Floating Point Math – https://0.30000000000000004.com](https://0.30000000000000004.com) 193 | - [确保前端 JavaScript 浮点数精度的四则运算方法](https://lzw.me/a/javascript-floating-point-arithmetic.html) 194 | 195 | ## License 196 | 197 | `@lzwme/asmd-calc` is released under the MIT license. 198 | 199 | 该插件由[志文工作室](https://lzw.me)开发和维护。 200 | 201 | [stars-badge]: https://img.shields.io/github/stars/lzwme/asmd-calc.svg 202 | [stars-url]: https://github.com/lzwme/asmd-calc/stargazers 203 | [forks-badge]: https://img.shields.io/github/forks/lzwme/asmd-calc.svg 204 | [forks-url]: https://github.com/lzwme/asmd-calc/network 205 | [issues-badge]: https://img.shields.io/github/issues/lzwme/asmd-calc.svg 206 | [issues-url]: https://github.com/lzwme/asmd-calc/issues 207 | [npm-badge]: https://img.shields.io/npm/v/@lzwme/asmd-calc.svg?style=flat-square 208 | [npm-url]: https://npmjs.org/package/@lzwme/asmd-calc 209 | [node-badge]: https://img.shields.io/badge/node.js-%3E=_10.9.0-green.svg?style=flat-square 210 | [node-url]: https://nodejs.org/download/ 211 | [download-badge]: https://img.shields.io/npm/dm/@lzwme/asmd-calc.svg?style=flat-square 212 | [download-url]: https://npmjs.org/package/@lzwme/asmd-calc 213 | [bundlephobia-url]: https://bundlephobia.com/result?p=@lzwme/asmd-calc@latest 214 | [bundlephobia-badge]: https://badgen.net/bundlephobia/minzip/@lzwme/asmd-calc@latest 215 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | [![@lzwme/asmd-calc](https://nodei.co/npm/@lzwme/asmd-calc.png)][download-url] 2 | 3 | # @lzwme/asmd-calc 4 | 5 | [![build status](https://github.com/lzwme/asmd-calc/actions/workflows/node-ci.yml/badge.svg)](https://github.com/lzwme/asmd-calc/actions/workflows/node-ci.yml) 6 | [![NPM version][npm-badge]][npm-url] 7 | [![node version][node-badge]][node-url] 8 | [![npm download][download-badge]][download-url] 9 | [![GitHub issues][issues-badge]][issues-url] 10 | [![GitHub forks][forks-badge]][forks-url] 11 | [![GitHub stars][stars-badge]][stars-url] 12 | ![license MIT](https://img.shields.io/github/license/lzwme/asmd-calc) 13 | 14 | 15 | 16 | 一个短小精悍的四则运算 JS 工具方法库,支持浮点数精度的加减乘除计算。 17 | 18 | 提供常见的加(`add`)、减(`sub`)、乘(`mul`)、除(`div`)计算方法,可满足涉及金额、价格处理等前端数据计算的大多数应用场景。 19 | 20 | **为什么选用 `asmd-calc`:** 21 | 22 | - 短小精悍。仅包含加减乘除计算,核心代码不足百行,无其他依赖,文件小巧够用 23 | - 执行性能高。仅考虑前端可精确表达的四则运算,实现逻辑简洁,执行性能优于功能丰富复杂的开源库(详见 Benchmark 部分) 24 | - 准确稳定。数个金融类交易系统多年开发实践经验的积累,覆盖了常见的各种计算用例误差场景 25 | 26 | **不适用的情况:** 27 | 28 | - 对于涉及超大数等`前端无法精确表示的数值处理`,建议由后端语言计算并使用字符串方式返回给前端展示。也可使用 `math.js` 等开源库以字符串形式执行计算。 29 | - 对于较为`复杂的数学科学计算`需求,推荐使用开源库如 [math.js](https://mathjs.org/index.html)、[decimal.js](https://github.com/MikeMcl/decimal.js)、[calculatorjs](https://github.com/fzred/calculatorjs)、[bignumber.js](https://github.com/MikeMcl/bignumber.js)、[big.js/](https://github.com/MikeMcl/big.js/)等,具体可分别参见其官方文档。 30 | 31 | ## 安装 32 | 33 | ```bash 34 | # npm 35 | npm i @lzwme/asmd-calc 36 | # yarn 37 | pnpm add @lzwme/asmd-calc 38 | # pnpm 39 | pnpm add @lzwme/asmd-calc 40 | ``` 41 | 42 | ## 用法 (USEAGE) 43 | 44 | ### 1. 使用简单的工具方法计算示例 45 | 46 | #### es module 47 | 48 | ```js 49 | import { add } from '@lzwme/asmd-calc'; 50 | 51 | console.log(add(0.1, 0.2, 0.3)); 52 | // => 0.6 53 | ``` 54 | 55 | 或 56 | 57 | ```js 58 | import * as calc from '@lzwme/asmd-calc'; 59 | 60 | console.log(calc.add(0.1, 0.2, 0.3)); 61 | // => 0.6 62 | ``` 63 | 64 | #### commonjs 65 | 66 | ```js 67 | const calc = require('@lzwme/asmd-calc'); 68 | 69 | console.log(calc.add(0.1, 0.2, 0.3)); 70 | // => 0.6 71 | ``` 72 | 73 | ### 2. 使用链式操作类计算示例 74 | 75 | #### es module 76 | 77 | ```js 78 | import { AsmdCalc } from '@lzwme/asmd-calc'; 79 | 80 | const a = new AsmdCalc(0); 81 | console.log(+a.add(0.1).add(0.2, 0.3)); 82 | // => 0.6 83 | 84 | const b = new AsmdCalc(0.3); 85 | b.add(0.1, 0.2).add(0.1).sub(0.1, 0.2).sub(0.1).div(0.3).mul(0.3); 86 | console.log(+b); 87 | // => 0.3 88 | console.log(b.value); 89 | // => 0.3 90 | ``` 91 | 92 | #### commonjs 93 | 94 | ```js 95 | const AsmdCalc = require('@lzwme/asmd-calc').AsmdCalc; 96 | 97 | const calc = new AsmdCalc(0); 98 | console.log(calc.add(0.1).add(0.2, 0.3)); 99 | // => 0.6 100 | ``` 101 | 102 | ## API 103 | 104 | - `add(...args)` 加法运算 105 | - `sub(...args)` 减法运算 106 | - `mul(...args)` 乘法运算 107 | - `div(...args)` 除法运算 108 | - `keepDotLength(value: number | string, precision: number, type: 'ceil' | 'round' | 'fround' | 'floor' | boolean = 'floor'): number | null` 保留 N 位小数,返回数值 109 | - `toFixed(value: number | string, precision: number, type: 'ceil' | 'round' | 'fround' | 'floor' | boolean = 'floor'): number | null` 保留 N 位小数(默认四舍五入[floor],返回字符串) 110 | - `getDecimalLen(num)` 获取指定数值的小数位长度 111 | - `toNonExponential(num)` 将指定的浮点数转换为非科学计数法的字符串格式 112 | 113 | ## 开发与测试 114 | 115 | - 开发 116 | 117 | ```bash 118 | pnpm install 119 | pnpm start 120 | ``` 121 | 122 | - 测试 123 | 124 | ```bash 125 | # 单元测试 126 | npm test 127 | 128 | # 基准测试 129 | npm run benchmark 130 | ``` 131 | 132 | - 构建 133 | 134 | ```bash 135 | npm run build 136 | ``` 137 | 138 | ## Benchmark 139 | 140 | ```bash 141 | npm run benchmark 142 | ``` 143 | 144 | 基准测试代码参见:[Benchmark](https://github.com/lzwme/asmd-calc/blob/master/benchmark/index.ts) 145 | 146 | 以下结果为在同一机器上分别执行 `10000*N` 次的耗时对比: 147 | 148 | | type/times | jsRaw | asmd-calc | mathjs | 149 | | ---------- | -------- | --------- | --------- | 150 | | add-10000 | 19.225ms | 169.535ms | 415.145ms | 151 | | sub-10000 | 16.269ms | 34.827ms | 171.263ms | 152 | | mul-10000 | 18.518ms | 51.625ms | 235.868ms | 153 | | div-10000 | 27.025ms | 79.504ms | 300.706ms | 154 | 155 | 预执行 `100_000` 次后再执行 `10000*N` 次的耗时对比: 156 | 157 | | type/times | jsRaw | asmd-calc | mathjs | 158 | | ---------- | -------- | --------- | --------- | 159 | | add-10000 | 7.768ms | 155.836ms | 362.819ms | 160 | | sub-10000 | 8.339ms | 25.147ms | 155.611ms | 161 | | mul-10000 | 9.995ms | 35.685ms | 224.357ms | 162 | | div-10000 | 15.666ms | 77.407ms | 280.322ms | 163 | 164 | ### Benchmark Details 165 | 166 | 随机数据集(测试用例): 167 | 168 | | type/times | [jsRawCalc] | [asmdCalc] | [decimal] | [mathjs] | 169 | | ---------- | ----------- | ---------- | --------- | --------- | 170 | | add-10000 | 4.973ms | 144.934ms | 192.637ms | 363.513ms | 171 | | sub-10000 | 6.971ms | 21.84ms | 65.373ms | 165.045ms | 172 | | mul-10000 | 8.45ms | 36.014ms | 107.898ms | 223.708ms | 173 | | div-10000 | 14.427ms | 64.409ms | 154.766ms | 290.645ms | 174 | 175 | 纯小数数据集: 176 | 177 | | type/times | [jsRawCalc] | [asmdCalc] | [decimal] | [mathjs] | 178 | | ---------- | ----------- | ---------- | --------- | -------- | 179 | | add-10000 | 0.248ms | 16.958ms | 30.875ms | 55.538ms | 180 | | sub-10000 | 0.462ms | 22.529ms | 32.719ms | 46.321ms | 181 | | mul-10000 | 0.232ms | 18.006ms | 34.765ms | 46.195ms | 182 | | div-10000 | 0.519ms | 17.37ms | 36.031ms | 47.271ms | 183 | 184 | 纯整数数据集: 185 | 186 | | type/times | [jsRawCalc] | [asmdCalc] | [decimal] | [mathjs] | 187 | | ---------- | ----------- | ---------- | --------- | -------- | 188 | | add-10000 | 0.172ms | 0.681ms | 10.248ms | 35.004ms | 189 | | sub-10000 | 0.816ms | 1.069ms | 12.736ms | 32.836ms | 190 | | mul-10000 | 0.178ms | 0.733ms | 13.827ms | 33.486ms | 191 | | div-10000 | 0.488ms | 0.699ms | 19.847ms | 42.217ms | 192 | 193 | ## 相关参考 194 | 195 | - [确保前端 JavaScript 浮点数精度的四则运算方法](https://lzw.me/a/javascript-floating-point-arithmetic.html) 196 | 197 | ## License 198 | 199 | `@lzwme/asmd-calc` is released under the MIT license. 200 | 201 | 该插件由[志文工作室](https://lzw.me)开发和维护。 202 | 203 | [stars-badge]: https://img.shields.io/github/stars/lzwme/asmd-calc.svg 204 | [stars-url]: https://github.com/lzwme/asmd-calc/stargazers 205 | [forks-badge]: https://img.shields.io/github/forks/lzwme/asmd-calc.svg 206 | [forks-url]: https://github.com/lzwme/asmd-calc/network 207 | [issues-badge]: https://img.shields.io/github/issues/lzwme/asmd-calc.svg 208 | [issues-url]: https://github.com/lzwme/asmd-calc/issues 209 | [npm-badge]: https://img.shields.io/npm/v/@lzwme/asmd-calc.svg?style=flat-square 210 | [npm-url]: https://npmjs.org/package/@lzwme/asmd-calc 211 | [node-badge]: https://img.shields.io/badge/node.js-%3E=_10.9.0-green.svg?style=flat-square 212 | [node-url]: https://nodejs.org/download/ 213 | [download-badge]: https://img.shields.io/npm/dm/@lzwme/asmd-calc.svg?style=flat-square 214 | [download-url]: https://npmjs.org/package/@lzwme/asmd-calc 215 | [bundlephobia-url]: https://bundlephobia.com/result?p=@lzwme/asmd-calc@latest 216 | [bundlephobia-badge]: https://badgen.net/bundlephobia/minzip/@lzwme/asmd-calc@latest 217 | -------------------------------------------------------------------------------- /benchmark/benchmark.ts: -------------------------------------------------------------------------------- 1 | import * as asmdCalc from '../src/lib/calculation'; 2 | 3 | function benchmark(desc: string, fn: () => unknown, times = 10_000) { 4 | // 预执行 N 次 5 | let preTimes = 100_000; 6 | while (preTimes--) fn(); 7 | 8 | const startTime = process.hrtime.bigint(); 9 | const label = `benchmark-${desc}-${times}`; 10 | 11 | console.time(label); 12 | while (times--) fn(); 13 | console.timeEnd(label); 14 | 15 | const timeCost = Number(process.hrtime.bigint() - startTime) / 1_000_000; 16 | // console.log(label, ':', timeCost, 'ms'); 17 | 18 | return timeCost; 19 | } 20 | 21 | export function benchmarkStart(calc: Partial = asmdCalc, desc: string, times = 10_000) { 22 | let fns = { 23 | add: () => { 24 | const list = [ 25 | { param: [0.1, 0.2], value: 0.3 }, 26 | { param: [1, 2, 0.1, 0.2, 0.3], value: 3.6 }, 27 | { param: [null, 1.1, 2.2, 3, 4], value: 10.3 }, 28 | { param: [void 0, 1, 1, 1.2, 1.1], value: 4.3 }, 29 | { param: [1.11111, 2.999], value: 4.11011 }, 30 | { param: [111111, 299900, 0.1, 0.2], value: 411011.3 }, 31 | { param: [1e-10, 2e-10], value: 3e-10 }, 32 | ]; 33 | 34 | list.forEach((item) => { 35 | calc.add(...item.param); 36 | }); 37 | }, 38 | sub: () => { 39 | const list = [ 40 | [3, 2, 1], 41 | [null, 3, -3], 42 | [1, null, 1], 43 | [void 0, 3, -3], 44 | [3, void 0, 3], 45 | [4.11011, 1.11111, 2.999], 46 | [411011, 111111, 299900], 47 | ]; 48 | 49 | list.forEach((item) => { 50 | calc.sub(item[0], item[1]); 51 | }); 52 | }, 53 | mul: () => { 54 | const list = [ 55 | [3, 2, 6], 56 | [null, 3, 0], 57 | [1, null, 0], 58 | [void 0, 3, 0], 59 | [3, void 0, 0], 60 | [1.11, 1.11, 1.2321], 61 | [111, 111, 12321], 62 | ['10.00', '10.1', 101], 63 | [4.11011, 100000, 411011], 64 | ]; 65 | 66 | list.forEach((item) => { 67 | calc.mul(item[0], item[1]); 68 | }); 69 | }, 70 | div: () => { 71 | const list = [ 72 | [0, 0, NaN], 73 | [6, 3, 2], 74 | [6, 0, Infinity], 75 | [null, 3, 0], 76 | [1, null, Infinity], 77 | [void 0, 3, 0], 78 | [3, void 0, Infinity], 79 | [1.2321, 1.11, 1.11], 80 | [12321, 111, 111], 81 | [101, '10.00', 10.1], 82 | [4.86111, 100, 0.0486111], 83 | ] as const; 84 | 85 | list.forEach((item) => { 86 | calc.div(item[0], item[1]); 87 | }); 88 | }, 89 | }; 90 | const result = { 91 | random: {} as Record, 92 | decimal: {} as Record, 93 | integer: {} as Record, 94 | times, 95 | }; 96 | 97 | console.log(`[${desc}]benchmark:`); 98 | for (const type in fns) result.random[type] = benchmark(`${desc}-${type}`, fns[type], times); 99 | console.log(); 100 | 101 | fns = { 102 | add: () => { 103 | calc.add(0.1, 0.2, 0.3); 104 | }, 105 | sub: () => { 106 | calc.sub(0.3, 0.2, 0.1); 107 | }, 108 | mul: () => { 109 | calc.mul(0.1, 0.2, 0.3); 110 | }, 111 | div: () => { 112 | calc.div(0.6, 0.1, 0.2); 113 | }, 114 | }; 115 | console.log(`[${desc}]benchmark-decimal:`); 116 | for (const type in fns) result.decimal[type] = benchmark(`${desc}-${type}`, fns[type], times); 117 | console.log(); 118 | 119 | fns = { 120 | add: () => { 121 | calc.add(100, 200, 300); 122 | }, 123 | sub: () => { 124 | calc.sub(300, 200, 100); 125 | }, 126 | mul: () => { 127 | calc.mul(100, 200, 300); 128 | }, 129 | div: () => { 130 | calc.div(600, 100, 200); 131 | }, 132 | }; 133 | 134 | console.log(`[${desc}]benchmark-integer:`); 135 | for (const type in fns) result.integer[type] = benchmark(`${desc}-${type}`, fns[type], times); 136 | console.log(); 137 | 138 | return result; 139 | } 140 | -------------------------------------------------------------------------------- /benchmark/calc/decimal-calc.ts: -------------------------------------------------------------------------------- 1 | import { Decimal } from 'decimal.js'; 2 | 3 | function fixInputValue(value) { 4 | // return Number.isNaN(value) || value === '-' || !value ? 0 : value; 5 | return Number.isNaN(value) || value === '' || value === '-' || !value ? 0 : value ?? 0; 6 | } 7 | 8 | export function Deci(value?: number | string | Decimal) { 9 | return new Decimal(fixInputValue(value)); 10 | } 11 | 12 | export { Decimal }; 13 | 14 | type FuncType = 'add' | 'div' | 'mul' | 'sub' | 'mod'; 15 | type ArgsMutiItem = number | string | Decimal; 16 | type ArgsMuti = ArgsMutiItem[]; 17 | 18 | function calc(funcType: FuncType, args: ArgsMuti) { 19 | let valChain = Deci(args[0]); 20 | 21 | args.slice(1).forEach((arg) => { 22 | valChain = valChain[funcType](fixInputValue(arg)); 23 | }); 24 | 25 | return valChain.toNumber(); 26 | } 27 | 28 | /** 加法 */ 29 | export function add(...args: ArgsMuti) { 30 | return calc('add', args); 31 | } 32 | /** 减法 */ 33 | export function sub(...args: ArgsMuti) { 34 | return calc('sub', args); 35 | } 36 | /** 乘法 */ 37 | export function mul(...args: ArgsMuti) { 38 | return calc('mul', args); 39 | } 40 | /** 除法 */ 41 | export function div(...args: ArgsMuti) { 42 | return calc('div', args); 43 | } 44 | /** 取余 */ 45 | export function mod(...args: ArgsMuti) { 46 | return calc('mod', args); 47 | } 48 | export function toFixed(x: number, n: number) { 49 | return Deci(x).toFixed(fixInputValue(n)); 50 | } 51 | 52 | /** 小于 */ 53 | export function smaller(a: ArgsMutiItem, b: ArgsMutiItem): boolean { 54 | return Deci(a).lt(b); 55 | } 56 | /** 小于等于 */ 57 | export function smallerEq(a: ArgsMutiItem, b: ArgsMutiItem): boolean { 58 | return Deci(a).lte(b); 59 | } 60 | /** 大于 */ 61 | export function larger(a: ArgsMutiItem, b: ArgsMutiItem): boolean { 62 | return Deci(a).gt(b); 63 | } 64 | /** 大于等于 */ 65 | export function largerEq(a: ArgsMutiItem, b: ArgsMutiItem): boolean { 66 | return Deci(a).gte(b); 67 | } 68 | 69 | // ---- 原型扩展兼容 ---- 70 | // Number.prototype.div = function (...args: ArgsMuti) { 71 | // return div(this.valueOf(), ...args); 72 | // }; 73 | 74 | // Number.prototype.mul = function (...args: ArgsMuti) { 75 | // return mul(this.valueOf(), ...args); 76 | // }; 77 | 78 | // Number.prototype.add = function (...args: ArgsMuti) { 79 | // return add(this.valueOf(), ...args); 80 | // }; 81 | 82 | // Number.prototype.sub = function (...args: ArgsMuti) { 83 | // return sub(this.valueOf(), ...args); 84 | // }; 85 | -------------------------------------------------------------------------------- /benchmark/calc/js-raw-calc.ts: -------------------------------------------------------------------------------- 1 | type ArgsMutiItem = number | string; 2 | type ArgsMuti = ArgsMutiItem[]; 3 | 4 | /** 加法 */ 5 | export function add(...args: ArgsMuti) { 6 | let total = 0; 7 | args.forEach((value) => { 8 | if (value != null) total += Number(value); 9 | }); 10 | return total; 11 | } 12 | /** 减法 */ 13 | export function sub(...args: ArgsMuti) { 14 | let total = Number(args[0]) || 0; 15 | args.slice(1).forEach((value) => { 16 | if (value != null) total -= Number(value); 17 | }); 18 | return total; 19 | } 20 | /** 乘法 */ 21 | export function mul(...args: ArgsMuti) { 22 | if (!args.length) return 0; 23 | let total = 1; 24 | args.forEach((value) => { 25 | if (value != null) total *= Number(value); 26 | }); 27 | return total; 28 | } 29 | /** 除法 */ 30 | export function div(...args: ArgsMuti) { 31 | let total = Number(args[0]) || 0; 32 | if (!total) return 0; 33 | 34 | args.slice(1).forEach((value) => { 35 | if (value != null) total /= Number(value); 36 | }); 37 | return total; 38 | } 39 | -------------------------------------------------------------------------------- /benchmark/calc/mathjs-calc.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lzw 3 | * @Date: 2021-03-12 12:37:12 4 | * @LastEditors: lzw 5 | * @LastEditTime: 2022-06-23 16:52:15 6 | * @Description: 基于 math.js 的加减乘除基本运算 7 | */ 8 | 9 | import { bignumber, chain } from 'mathjs'; 10 | 11 | type FuncType = 'add' | 'divide' | 'multiply' | 'subtract' | 'mod'; 12 | type ArgsMutiItem = number | string; 13 | type ArgsMuti = ArgsMutiItem[]; 14 | 15 | function calc(funcType: FuncType, args: ArgsMuti) { 16 | let valChain = chain(bignumber(+args[0])); 17 | 18 | args.slice(1).forEach((arg) => { 19 | valChain = valChain[funcType](bignumber(+arg)); 20 | }); 21 | 22 | return +valChain.done(); 23 | } 24 | 25 | /** 加法 */ 26 | export function add(...args: ArgsMuti) { 27 | return calc('add', args); 28 | } 29 | /** 减法 */ 30 | export function sub(...args: ArgsMuti) { 31 | return calc('subtract', args); 32 | } 33 | /** 乘法 */ 34 | export function mul(...args: ArgsMuti) { 35 | return calc('multiply', args); 36 | } 37 | /** 除法 */ 38 | export function div(...args: ArgsMuti) { 39 | return calc('divide', args); 40 | } 41 | /** 取余 */ 42 | export function mod(...args: ArgsMuti) { 43 | return calc('mod', args); 44 | } 45 | /** 小于 */ 46 | export function smaller(a: ArgsMutiItem, b: ArgsMutiItem) { 47 | return chain(bignumber(a)).smaller(bignumber(b)).done(); 48 | } 49 | /** 小于等于 */ 50 | export function smallerEq(a: ArgsMutiItem, b: ArgsMutiItem) { 51 | return chain(bignumber(a)).smallerEq(bignumber(b)).done(); 52 | } 53 | /** 大于 */ 54 | export function larger(a: ArgsMutiItem, b: ArgsMutiItem) { 55 | return chain(bignumber(a)).larger(bignumber(b)).done(); 56 | } 57 | /** 大于等于 */ 58 | export function largerEq(a: ArgsMutiItem, b: ArgsMutiItem) { 59 | return chain(bignumber(a)).largerEq(bignumber(b)).done(); 60 | } 61 | 62 | // Number.prototype.div = function (...args: ArgsMuti) { 63 | // return div(this.valueOf(), ...args); 64 | // }; 65 | 66 | // Number.prototype.mul = function (...args: ArgsMuti) { 67 | // return mul(this.valueOf(), ...args); 68 | // }; 69 | 70 | // Number.prototype.add = function (...args: ArgsMuti) { 71 | // return add(this.valueOf(), ...args); 72 | // }; 73 | 74 | // Number.prototype.sub = function (...args: ArgsMuti) { 75 | // return sub(this.valueOf(), ...args); 76 | // }; 77 | -------------------------------------------------------------------------------- /benchmark/index.ts: -------------------------------------------------------------------------------- 1 | import * as asmdCalc from '../src/lib/calculation'; 2 | import * as jsRawCalc from './calc/js-raw-calc'; 3 | import * as mathjsCalc from './calc/mathjs-calc'; 4 | import * as decimalCalc from './calc/decimal-calc'; 5 | import { benchmarkStart } from './benchmark'; 6 | 7 | function doBenchmark() { 8 | const jsResult = benchmarkStart(jsRawCalc, 'jsRawCalc'); 9 | console.log('----------------------------------------'); 10 | const asmdResult = benchmarkStart(asmdCalc, 'asmdCalc'); 11 | console.log('----------------------------------------'); 12 | const mathjsResult = benchmarkStart(mathjsCalc, 'mathjs'); 13 | console.log('----------------------------------------'); 14 | const decimalResult = benchmarkStart(decimalCalc, 'decimal'); 15 | 16 | const result = { jsRaw: jsResult, asmd: asmdResult, decimal: decimalResult, mathjs: mathjsResult }; 17 | console.log(result); 18 | // print for markdown table 19 | const testTypes = Object.keys(result.jsRaw).filter((k) => k !== 'times'); 20 | 21 | testTypes.forEach((type) => { 22 | console.log(`\n\`${type}-${result.jsRaw.times}times\`:\n`); 23 | console.log('| type/time |', Object.keys(result).join(' | '), ' |'); 24 | console.log(`| --- | --- | --- | --- | --- |`); 25 | ['add', 'sub', 'mul', 'div'].forEach((key) => { 26 | console.log( 27 | `| ${key} |`, 28 | Object.values(result) 29 | .map((d) => d[type][key] + 'ms') 30 | .join(' | '), 31 | ' | ' 32 | ); 33 | }); 34 | }); 35 | } 36 | 37 | doBenchmark(); 38 | -------------------------------------------------------------------------------- /eslint.config.cjs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: renxia 3 | * @Date: 2024-04-10 10:10:45 4 | * @LastEditors: renxia 5 | * @LastEditTime: 2025-04-02 16:46:22 6 | * @Description: 7 | */ 8 | // module.exports = [ 9 | // ...require('./preset/eslint'), 10 | // ]; 11 | const path = require('path'); 12 | const eslint = require('@eslint/js'); 13 | const tseslint = require('typescript-eslint'); 14 | const globals = require('globals'); 15 | 16 | module.exports = [ 17 | ...tseslint.config( 18 | eslint.configs.recommended, 19 | ...tseslint.configs.recommendedTypeChecked, 20 | { 21 | languageOptions: { 22 | globals: { 23 | ...globals.node, 24 | }, 25 | parserOptions: { 26 | tsconfigRootDir: path.resolve(__dirname), 27 | project: path.resolve('./tsconfig.eslint.json'), 28 | projectFolderIgnoreList: ['**/node_modules/**', '**/dist/**'], 29 | warnOnUnsupportedTypeScriptVersion: false, 30 | ecmaFeatures: { 31 | jsx: true, 32 | }, 33 | ecmaVersion: 2023, 34 | sourceType: 'module', 35 | }, 36 | }, 37 | linterOptions: { 38 | reportUnusedDisableDirectives: false, 39 | }, 40 | plugins: { 41 | prettier: require('eslint-plugin-prettier'), 42 | // unicorn: require('eslint-plugin-unicorn'), 43 | }, 44 | ignores: ['**/node_modules/**', 'dist/**', 'cjs/**', 'esm/**', 'docs/**', 'mock/**', '**/*.js', '**/*.d.ts'], 45 | rules: { 46 | 'prettier/prettier': 'warn', 47 | indent: ['off', 2], 48 | 'no-console': ['error', { allow: ['warn', 'error'] }], 49 | '@typescript-eslint/explicit-module-boundary-types': 'off', 50 | '@typescript-eslint/no-unused-vars': ['warn', { varsIgnorePattern: '^_', argsIgnorePattern: '^_', ignoreRestSiblings: true }], 51 | // TODO 52 | '@typescript-eslint/no-unsafe-member-access': 'off', 53 | '@typescript-eslint/no-unsafe-assignment': 'off', 54 | '@typescript-eslint/no-unsafe-argument': 'off', 55 | '@typescript-eslint/no-floating-promises': 'off', 56 | '@typescript-eslint/ban-ts-comment': 'off', 57 | '@typescript-eslint/no-unsafe-call': 'off', 58 | '@typescript-eslint/no-unnecessary-type-assertion': 'off', 59 | '@typescript-eslint/no-misused-promises': 'off', 60 | '@typescript-eslint/unbound-method': 'off', 61 | '@typescript-eslint/await-thenable': 'off', 62 | '@typescript-eslint/no-unsafe-return': 'off', 63 | }, 64 | files: ['src/**/*.ts'], 65 | }, 66 | { 67 | files: ['*.js', '*.cjs', '*.mjs'], 68 | ...tseslint.configs.disableTypeChecked, 69 | }, 70 | ), 71 | ]; 72 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import('@jest/types').Config.InitialOptions } */ 4 | const config = { 5 | cache: true, 6 | // preset: 'ts-jest', 7 | // globals: { 8 | // 'ts-jest': { 9 | // tsconfig: 'tsconfig.module.json', 10 | // }, 11 | // }, 12 | transform: { 13 | '^.+\\.(t|j)sx?$': [ 14 | '@swc/jest', 15 | { 16 | jsc: { 17 | target: 'es2022', 18 | }, 19 | }, 20 | ], 21 | }, 22 | extensionsToTreatAsEsm: ['.ts'], 23 | 24 | testEnvironment: 'node', 25 | testMatch: ['/src/__test__/*.spec.ts'], 26 | coveragePathIgnorePatterns: ['/node_modules/', 'src/cli.ts', 'src/index.ts', 'src/__test__'], 27 | collectCoverageFrom: ['src/**/!(*.d).ts'], 28 | // maxWorkers: require('os').cpus().length, 29 | // watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'], 30 | }; 31 | // module.exports = config; 32 | export default config; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lzwme/asmd-calc", 3 | "version": "1.3.4", 4 | "description": "支持浮点数精度的加减乘除四则运算 JS 库。", 5 | "main": "build/main/index.js", 6 | "browser": "build/main/index.js", 7 | "module": "build/module/index.js", 8 | "typings": "build/main/index.d.ts", 9 | "private": false, 10 | "license": "MIT", 11 | "repository": "https://github.com/lzwme/asmd-calc.git", 12 | "author": { 13 | "name": "renxia", 14 | "email": "lzwy0820@qq.com", 15 | "url": "https://lzw.me" 16 | }, 17 | "engines": { 18 | "node": ">=10" 19 | }, 20 | "publishConfig": { 21 | "access": "public", 22 | "registry": "https://registry.npmjs.com" 23 | }, 24 | "files": [ 25 | "build", 26 | "REDME.md", 27 | "README-zh-CN.md" 28 | ], 29 | "keywords": [ 30 | "calc", 31 | "precision", 32 | "number", 33 | "decimal", 34 | "float", 35 | "floating-point", 36 | "浮点数计算", 37 | "浮点数精确计算" 38 | ], 39 | "scripts": { 40 | "prepare": "husky || true", 41 | "start": "npm run watch", 42 | "build": "run-s clean && run-p build:*", 43 | "build:main": "tsc -p tsconfig.cjs.json", 44 | "build:module": "tsc -p tsconfig.module.json", 45 | "fix": "run-s fix:*", 46 | "fix:lint": "eslint src/**/*.ts --fix", 47 | "fix:prettier": "prettier \"src/**/*.ts\" --write", 48 | "test": "run-s test:*", 49 | "test:lint": "eslint src/*/*.ts", 50 | "test:prettier": "prettier \"src/**/*.ts\" --list-different", 51 | "test:unit": "npm run cov", 52 | "watch": "run-s clean build:main && run-p \"build:main -- -w\" \"test:unit -- --watch\"", 53 | "watch:build": "tsc -p tsconfig.json -w", 54 | "watch:test": "jest --watch", 55 | "cov": "jest --coverage --silent", 56 | "cov:html": "jest --coverage --silent --reporter=html", 57 | "doc": "run-s doc:html", 58 | "doc:html": "typedoc src/ --exclude **/*.spec.ts --out docs --tsconfig tsconfig.module.json", 59 | "doc:json": "typedoc src/ --exclude **/*.spec.ts --json docs/typedoc.json --tsconfig tsconfig.module.json", 60 | "version": "standard-version", 61 | "clean": "flh rm -f build", 62 | "release": "run-s build doc:html", 63 | "release-version": "run-s test release version", 64 | "benchmark": "ts-node -T benchmark/index.ts" 65 | }, 66 | "devDependencies": { 67 | "@eslint/js": "^9.23.0", 68 | "@jest/core": "^29", 69 | "@lzwme/fed-lint-helper": "^2.6.6", 70 | "@swc/core": "^1.11.16", 71 | "@swc/jest": "^0.2.37", 72 | "@types/eslint": "^9.6.1", 73 | "@types/jest": "^29.5.14", 74 | "@types/node": "^22", 75 | "@typescript-eslint/eslint-plugin": "^8.29.0", 76 | "@typescript-eslint/parser": "^8.29.0", 77 | "decimal.js": "^10.5.0", 78 | "eslint": "^9.23.0", 79 | "eslint-config-prettier": "^10.1.1", 80 | "eslint-plugin-eslint-comments": "^3.2.0", 81 | "eslint-plugin-functional": "^9.0.1", 82 | "eslint-plugin-import": "^2.31.0", 83 | "eslint-plugin-jest": "^28.11.0", 84 | "eslint-plugin-prettier": "^5.2.5", 85 | "husky": "^9.1.7", 86 | "jest": "^29", 87 | "mathjs": "^14.4.0", 88 | "npm-run-all": "^4.1.5", 89 | "prettier": "^3.5.3", 90 | "standard-version": "^9.5.0", 91 | "ts-node": "^10.9.2", 92 | "typedoc": "^0.28.1", 93 | "typescript": "^5.8.2", 94 | "typescript-eslint": "^8.29.0" 95 | }, 96 | "prettier": { 97 | "singleQuote": true 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/__test__/asmd-calc.spec.ts: -------------------------------------------------------------------------------- 1 | import { AsmdCalc } from '../lib/asmd-calc.class'; 2 | 3 | describe('class AsmdCalc', () => { 4 | it('AsmdCalc', () => { 5 | const a = new AsmdCalc(12); 6 | expect(+a).toBe(12); 7 | expect(a.value).toBe(12); 8 | 9 | const b = new AsmdCalc('15'); 10 | expect(+b).toBe(15); 11 | expect(b.value).toBe(15); 12 | 13 | const x = new AsmdCalc(0); 14 | expect(+x).toBe(0); 15 | expect(+x.add(1, 2).add(3)).toBe(6); 16 | }); 17 | 18 | it('AsmdCalc.add', () => { 19 | const a = new AsmdCalc(0.1); 20 | expect(+a).toBe(0.1); 21 | 22 | expect(+a.add(0).add(0.2).add(0.3)).toBe(0.6); 23 | }); 24 | 25 | it('AsmdCalc.sub', () => { 26 | const a = new AsmdCalc(0.3); 27 | expect(+a).toBe(0.3); 28 | expect(+a.sub(0.1)).toBe(0.2); 29 | expect(+a.sub(0.2)).toBe(0); 30 | 31 | expect(+a.mul(0).add(0.3).sub(0.1, 0.2)).toBe(0); 32 | expect(+a.sub(0.3).add(0.3)).toBe(0); 33 | }); 34 | 35 | it('AsmdCalc.mul', () => { 36 | const a = new AsmdCalc(0.1); 37 | expect(+a).toBe(0.1); 38 | 39 | expect(+a.mul(0.2).mul(2).mul(100)).toBe(4); 40 | 41 | expect( 42 | +a 43 | .mul(0) 44 | .add(0.1) // 重置为 0.1 45 | .mul(0.2, 2, 100), 46 | ).toBe(4); 47 | }); 48 | 49 | it('AsmdCalc.div', () => { 50 | const a = new AsmdCalc(0.3); 51 | expect(+a).toBe(0.3); 52 | 53 | expect(+a.div(0.1).div(0.2).div(0.3)).toBe(50); 54 | }); 55 | 56 | it('AsmdCalc.keepDotLength', () => { 57 | const a = new AsmdCalc(0.33366666); 58 | expect(+a).toBe(0.33366666); 59 | 60 | expect(+a.keepDotLength(6)).toBe(0.333666); 61 | expect(+a.keepDotLength(5, false)).toBe(0.33366); 62 | expect(+a.keepDotLength(4, true)).toBe(0.3337); 63 | }); 64 | 65 | it('AsmdCalc.toFixed', () => { 66 | const a = new AsmdCalc(1.45); 67 | expect(+a).toBe(1.45); 68 | 69 | expect(a.toFixed(1)).toBe('1.5'); 70 | expect(a.toFixed(2)).toBe('1.45'); 71 | expect(a.toFixed(3)).toBe('1.450'); 72 | }); 73 | 74 | it('AsmdCalc.toValue 应返回当前值', () => { 75 | const a = new AsmdCalc(1); 76 | expect(Number(a)).toBe(1); 77 | expect(Number(a)).toBe(a.value); 78 | }); 79 | 80 | it('AsmdCalc.toString 应返回当前值的非科学计数法字符串格式', () => { 81 | const a = new AsmdCalc(1); 82 | expect(String(a)).toBe('1'); 83 | a.mul(0.000000001); 84 | expect(String(a)).toBe('0.000000001'); 85 | }); 86 | 87 | it('AsmdCalc 各种链式计算', () => { 88 | const a = new AsmdCalc(0.3); 89 | a.sub(0.1, 0.2).add(0.1, 0.2).add(0.1).sub(0.1).div(0.3).mul(0.3); 90 | expect(+a).toBe(0.3); 91 | }); 92 | 93 | it('AsmdCalc 异常入参', () => { 94 | const a = new AsmdCalc(0.3); 95 | const b = new AsmdCalc(a); 96 | expect(+b).toBe(0.3); 97 | 98 | expect(() => { 99 | return new AsmdCalc({ a: 1 }); 100 | }).toThrow(); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /src/__test__/calculation.spec.ts: -------------------------------------------------------------------------------- 1 | import * as calc from '../lib/calculation'; 2 | const { toFixed } = calc; 3 | 4 | describe('calc', () => { 5 | it('addition 各种入参测试', () => { 6 | const list = [ 7 | [NaN, 3, 3], 8 | [3, NaN, 3], 9 | [null, 3, 3], 10 | [3, null, 3], 11 | [3, void 0, 3], 12 | [void 0, 3, 3], 13 | [0.1, 0.2, 0.3], 14 | [1, 2, 3], 15 | [1.11111, 2.999, 4.11011], 16 | [111111, 299900, 411011], 17 | [1e-10, 2e-10, 3e-10], 18 | ['1', '10', 11], 19 | ['3', 1.01, 4.01], 20 | [1.01, '3', 4.01], 21 | ]; 22 | 23 | list.forEach((item) => { 24 | expect(calc.add(item[0], item[1])).toBe(item[2]); 25 | }); 26 | }); 27 | 28 | it('addition 加法计算支持无限参数个数', () => { 29 | const list = [ 30 | { param: [0.1, 0.2], value: 0.3 }, 31 | { param: [1, 2, 0.1, 0.2, 0.3], value: 3.6 }, 32 | { param: [null, 1.1, 2.2, 3, 4], value: 10.3 }, 33 | { param: [void 0, 1, 1, 1.2, 1.1], value: 4.3 }, 34 | { param: [1.11111, 2.999], value: 4.11011 }, 35 | { param: [111111, 299900, 0.1, 0.2], value: 411011.3 }, 36 | { param: [1e-10, 2e-10], value: 3e-10 }, 37 | ]; 38 | 39 | list.forEach((item) => { 40 | expect(calc.add(...item.param)).toBe(item.value); 41 | }); 42 | }); 43 | 44 | it('addition 科学计数法数值测试', () => { 45 | const list = [ 46 | [1e2, 2e2, 300], 47 | [1e-2, 2e-2, 3e-2], 48 | [1e-10, 2e-10, 3e-10], 49 | [0.0000001, 2e-10, 0.0000001002], 50 | ]; 51 | 52 | list.forEach((item) => { 53 | expect(calc.add(item[0], item[1])).toBe(item[2]); 54 | }); 55 | }); 56 | 57 | it('subtraction 各种入参测试', () => { 58 | const list = [ 59 | [3, 2, 1], 60 | [null, 3, -3], 61 | [1, null, 1], 62 | [void 0, 3, -3], 63 | [3, void 0, 3], 64 | [4.11011, 1.11111, 2.999], 65 | [411011, 111111, 299900], 66 | ['10', '10', 0], 67 | [3.03, '3', 0.03], 68 | ['3.03', 3, 0.03], 69 | ]; 70 | 71 | list.forEach((item) => { 72 | expect(calc.sub(item[0], item[1])).toBe(item[2]); 73 | }); 74 | 75 | expect(calc.sub()).toBe(null); 76 | }); 77 | 78 | it('subtraction 减法计算支持无限参数个数', () => { 79 | const list = [ 80 | { param: [0.3, 0.1, 0.2], value: 0 }, 81 | { param: [3.6, 1, 2, 0.1, 0.2, 0.3], value: 0 }, 82 | { param: [10.3, null, 1.1, 2.2, 3, 4], value: 0 }, 83 | { param: [4.3, void 0, 1, 1, 1.2, 1.1], value: 0 }, 84 | { param: [4.11011, 1.11111, 2.999], value: 0 }, 85 | { param: [411011.3, 111111, 299900, 0.1, 0.2], value: 0 }, 86 | { param: [3e-10, 1e-10, 2e-10], value: 0 }, 87 | ]; 88 | 89 | list.forEach((item) => { 90 | expect(calc.sub(...item.param)).toBe(0); 91 | }); 92 | }); 93 | 94 | it('multiplication 各种入参测试', () => { 95 | const list = [ 96 | [3, 2, 6], 97 | [null, 3, 0], 98 | [1, null, 0], 99 | [void 0, 3, 0], 100 | [3, void 0, 0], 101 | [1.11, 1.11, 1.2321], 102 | [111, 111, 12321], 103 | ['10.00', '10.1', 101], 104 | [4.11011, 100000, 411011], 105 | [0.28, 100, 28], 106 | [0.58, 100, 58], 107 | ['10', '10', 100], 108 | [3.03, '3', 9.09], 109 | ['3.03', 3, 9.09], 110 | ]; 111 | 112 | list.forEach((item) => { 113 | expect(calc.mul(item[0], item[1])).toBe(item[2] as number); 114 | }); 115 | 116 | expect(calc.mul()).toBe(0); 117 | }); 118 | 119 | it('multiplication 乘法计算支持无限参数个数', () => { 120 | const list = [ 121 | { param: [3, 0.1, 0.2], value: 0.06 }, 122 | { param: [3.6, 1, 2, 0.1, 0.2, 0.3], value: 0.0432 }, 123 | { param: [10.3, null, 1.1, 2.2, 3, 4], value: 0 }, 124 | { param: [4.3, void 0, 1, 1, 1.2, 1.1], value: 0 }, 125 | { param: [4.11, 1.1, 2.999], value: 13.558479 }, 126 | { param: [3, 1.1, 290, 0.1, 0.2], value: 19.14 }, 127 | { param: [3e-10, 1e-10, 2e-10], value: 6e-30 }, 128 | ]; 129 | 130 | list.forEach((item) => { 131 | expect(calc.mul(...item.param)).toBe(item.value); 132 | }); 133 | }); 134 | 135 | it('division 各种入参测试', () => { 136 | const list = [ 137 | [0, 0, NaN], 138 | [6, 3, 2], 139 | [6, 0, Infinity], 140 | [null, 3, 0], 141 | [1, null, Infinity], 142 | [void 0, 3, NaN], 143 | [3, void 0, NaN], 144 | [1.2321, 1.11, 1.11], 145 | [12321, 111, 111], 146 | [101, '10.00', 10.1], 147 | [4.86111, 100, 0.0486111], 148 | ['10', '10', 1], 149 | [3.03, '3', 1.01], 150 | ['3.03', 3, 1.01], 151 | ] as const; 152 | 153 | list.forEach((item) => { 154 | expect(calc.div(item[0], item[1])).toBe(item[2]); 155 | }); 156 | 157 | expect(calc.div()).toBe(null); 158 | }); 159 | 160 | it('division 除法计算支持无限参数个数', () => { 161 | const list = [ 162 | { param: [0.06, 3, 0.1, 0.2], value: 1 }, 163 | { param: [0.0432, 3.6, 1, 2, 0.1, 0.2, 0.3], value: 1 }, 164 | { param: [10.3, null, 1.1, 2.2, 3, 4], value: Infinity }, 165 | { param: [4.3, void 0, 1, 1, 1.2, 1.1], value: NaN }, 166 | { param: [13.558479, 4.11, 1.1, 2.999], value: 1 }, 167 | { param: [19.14, 3, 1.1, 290, 0.1, 0.2], value: 1 }, 168 | { param: [6e-20, 3e-10, 2e-10], value: 1 }, 169 | { param: [null, 3e-10, 1e-10, 2e-10], value: 0 }, 170 | ]; 171 | 172 | list.forEach((item) => { 173 | expect(calc.div(...item.param)).toBe(item.value); 174 | }); 175 | }); 176 | 177 | it('keepDotLength 各种入参测试(截断模式)', () => { 178 | const list = [ 179 | [null, 3, null], 180 | [void 0, 3, null], 181 | [1, null, 1], 182 | [1, void 0, 1], 183 | [1.001, -1, 1], 184 | [1.001, null, 1], 185 | [1.001, void 0, 1], 186 | [6, 0, 6], 187 | [6, 10, 6], 188 | [6.999999, 2, 6.99], 189 | [99.999999, 2, 99.99], 190 | [6.1288, 0, 6], 191 | [6.1288, 1, 6.1], 192 | [6.1288, 2, 6.12], 193 | [6.1288, 3, 6.128], 194 | [6.1288, 10, 6.1288], 195 | ]; 196 | 197 | list.forEach((item) => { 198 | expect(calc.keepDotLength(item[0], item[1])).toBe(item[2]); 199 | }); 200 | }); 201 | 202 | it('keepDotLength 各种入参测试(四舍五入方式,等同于 toFixed 结果转整数)', () => { 203 | const list = [ 204 | [null, 3, null], 205 | [1, null, 1], 206 | [1.55, null, 2], 207 | [1.45, 1, 1.5], 208 | [1.55, 1, 1.6], 209 | [1.515, 2, 1.52], 210 | [6, 0, 6], 211 | [6, 10, 6], 212 | [6.1288, 0, 6], 213 | [6.1288, 1, 6.1], 214 | [6.1288, 2, 6.13], 215 | [6.999999, 2, 7], 216 | [99.999999, 2, 100], 217 | [16.1288, 3, 16.129], 218 | [1000000000006.1288, 10, 1000000000006.1288], 219 | ]; 220 | 221 | list.forEach((item) => { 222 | expect(calc.keepDotLength(item[0], item[1], true)).toBe(item[2]); 223 | }); 224 | }); 225 | }); 226 | 227 | describe('toFixed function', () => { 228 | it('toFixed - round 各种入参测试', () => { 229 | const list = [ 230 | [null, 3, null], 231 | [1, null, 1], 232 | [1.45, 1, 1.5], 233 | [1.55, 1, 1.6], 234 | [1.515, 2, 1.52], 235 | [6, 0, 6], 236 | [6, 5, '6.00000'], 237 | [6.1288, 0, 6], 238 | [6.1288, 1, 6.1], 239 | [6.1288, 2, 6.13], 240 | [6.999999, 2, '7.00'], 241 | [99.999999, 2, '100.00'], 242 | [16.1288, 3, 16.129], 243 | [1000000000006.1288, 0, '1000000000006'], 244 | [1000000000006.1288, 1, '1000000000006.1'], 245 | [1000000000006.1288, 2, '1000000000006.13'], 246 | [1000000000006.1288, 3, '1000000000006.129'], 247 | [1000000000006.1288, 4, '1000000000006.1288'], 248 | [1000000000006.1288, 5, '1000000000006.12880'], 249 | [0.00001811, 3, '0.000'], 250 | [-0.00001811, 3, '0.000'], // todo: -0.000 251 | [0.000000000011, 3, '0.000'], 252 | [141555511.82, 10, '141555511.8200000000'], 253 | ] as const; 254 | 255 | list.forEach((item) => { 256 | expect(calc.toFixed(item[0], item[1] as number)).toBe(null == item[2] ? item[2] : String(item[2])); 257 | expect(calc.toFixed(item[0], item[1] as number, 'round')).toBe(null == item[2] ? item[2] : String(item[2])); 258 | }); 259 | }); 260 | 261 | it('should return null when value is null', () => { 262 | expect(toFixed(null, 2)).toBeNull(); 263 | }); 264 | 265 | it('should throw error when type is invalid', () => { 266 | expect(() => toFixed(1.23, 2, 'invalid' as 'ceil')).toThrowError(); 267 | }); 268 | 269 | it('should handle precision 0 correctly', () => { 270 | expect(toFixed(3.14, 0, 'ceil')).toBe('4'); 271 | expect(toFixed(3.14, 0, 'floor')).toBe('3'); 272 | expect(toFixed(3.84, 0, 'floor')).toBe('3'); 273 | 274 | expect(toFixed(3.14, 0, 'round')).toBe('3'); 275 | expect(toFixed(3.84, 0, 'round')).toBe('4'); 276 | }); 277 | 278 | it('should handle numbers without fractional part', () => { 279 | expect(toFixed(123, 3)).toBe('123.000'); 280 | }); 281 | 282 | it('should handle numbers with fractional part', () => { 283 | expect(toFixed(1.2345, 3, 'ceil')).toBe('1.235'); 284 | expect(toFixed(1.2345, 3, 'floor')).toBe('1.234'); 285 | 286 | expect(toFixed(1.2341, 3, 'round')).toBe('1.234'); 287 | expect(toFixed(1.2345, 3, 'round')).toBe('1.235'); 288 | }); 289 | 290 | it('should handle numbers requiring rounding up', () => { 291 | expect(toFixed(0.999, 2, 'ceil')).toBe('1.00'); 292 | }); 293 | 294 | it('should handle numbers with exponential notation', () => { 295 | expect(toFixed(1.23e-5, 5)).toBe('0.00001'); 296 | }); 297 | 298 | it('should handle string input', () => { 299 | expect(toFixed('123.456', 2)).toBe('123.46'); 300 | }); 301 | }); 302 | -------------------------------------------------------------------------------- /src/__test__/utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { getDecimalLen, isDecimal, toNonExponential } from '../lib/utils'; 2 | 3 | describe('calc', () => { 4 | it('toNonExponential 将指定的数值转换为非科学计数法的字符串格式', () => { 5 | const toNExpList = [ 6 | [0, '0'], 7 | [null, '0'], 8 | [NaN, '0'], 9 | [void 0, '0'], 10 | [Infinity, 'Infinity'], 11 | [-Infinity, '-Infinity'], 12 | [100, '100'], 13 | [10e8, '1000000000'], 14 | [10e-10, '0.000000001'], 15 | [10.12e10, '101200000000'], 16 | [10.12e-10, '0.000000001012'], 17 | [-10.12e10, '-101200000000'], 18 | ]; 19 | 20 | toNExpList.forEach((item) => { 21 | expect(toNonExponential(item[0] as number)).toBe(item[1] as string); 22 | }); 23 | }); 24 | 25 | it('isDecimal', () => { 26 | const decimalList = ['0.00000001012', '100.1', 100.1, 0.0001, -0.1, -100.01, 0.3 - 0.2]; 27 | const aList = [100, 'a', 'bbb', 0, void 0, null, 0, NaN, '', 10001]; 28 | 29 | decimalList.forEach((val) => { 30 | expect(isDecimal(val)).toBeTruthy(); 31 | }); 32 | aList.forEach((val) => { 33 | expect(isDecimal(val)).toBeFalsy(); 34 | }); 35 | }); 36 | 37 | it('getDecimalLen', () => { 38 | expect(getDecimalLen(10)).toBe(0); 39 | expect(getDecimalLen(null)).toBe(0); 40 | expect(getDecimalLen(1e-9)).toBe(9); 41 | expect(getDecimalLen('abc')).toBe(0); 42 | expect(getDecimalLen('0.001')).toBe(3); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/asmd-calc.class'; 2 | export * from './lib/calculation'; 3 | export * from './lib/utils'; 4 | -------------------------------------------------------------------------------- /src/lib/asmd-calc.class.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable:typedef 2 | import { add, div, keepDotLength, mul, sub, toFixed } from './calculation'; 3 | import { toNonExponential } from './utils'; 4 | 5 | /** 6 | * 支持浮点数四则运算的链式操作类 7 | */ 8 | export class AsmdCalc { 9 | private total = 0; 10 | 11 | get value() { 12 | return this.total; 13 | } 14 | 15 | constructor(value) { 16 | if (!value) value = 0; 17 | 18 | const type = typeof value; 19 | 20 | if (value instanceof AsmdCalc) { 21 | this.total = value.total; 22 | } else if (type === 'number' || type === 'string') { 23 | this.total = Number(value); 24 | } else { 25 | throw Error('[AsmdCalcError] Invalid argument: ' + value); 26 | } 27 | 28 | return this; 29 | } 30 | /** 31 | * 加法 32 | * 33 | * ### Example (es module) 34 | * ```js 35 | * import { AsmdCalc } from 'asmd-calc'; 36 | * const a = new AsmdCalc(0.1); 37 | * console.log(+a.add(0.2, 3)); 38 | * // => 3.3 39 | * console.log(+a.add(0.2).add(3)); 40 | * // => 6.5 41 | * ``` 42 | * 43 | * @param args 44 | */ 45 | public add(...args) { 46 | this.total = add(this.total, ...args); 47 | return this; 48 | } 49 | /** 50 | * 减法 51 | * 52 | * ### Example (es module) 53 | * ```js 54 | * import { AsmdCalc } from 'asmd-calc'; 55 | * const a = new AsmdCalc(0.3); 56 | * console.log(+a.sub(0.1, 0.2)); 57 | * // => 0 58 | * console.log(+a.sub(0.3).add(0.3)); 59 | * // => 0 60 | * ``` 61 | * 62 | * @param args 63 | */ 64 | public sub(...args) { 65 | this.total = sub(this.total, ...args); 66 | return this; 67 | } 68 | /** 69 | * 乘法 70 | * 71 | * ### Example (es module) 72 | * ```js 73 | * import { AsmdCalc } from 'asmd-calc'; 74 | * const a = new AsmdCalc(0.1); 75 | * console.log(+a.mul(0.2)); 76 | * // => 0.02 77 | * console.log(+a.mul(0.3).mul(30)); 78 | * // => 0.18 79 | * ``` 80 | * 81 | * @param args 82 | */ 83 | public mul(...args) { 84 | this.total = mul(this.total, ...args); 85 | return this; 86 | } 87 | /** 88 | * 除法 89 | * 90 | * ### Example (es module) 91 | * ```js 92 | * import { AsmdCalc } from 'asmd-calc'; 93 | * const a = new AsmdCalc(0.3); 94 | * console.log(+a.div(0.1, 0.2).div(0.3)); 95 | * // => 50 96 | * ``` 97 | * 98 | * @param args 99 | */ 100 | public div(...args) { 101 | this.total = div(this.total, ...args); 102 | return this; 103 | } 104 | /** 105 | * 最多保留 N 位小数 106 | * 107 | * ### Example (es module) 108 | * ```js 109 | * import { AsmdCalc } from 'asmd-calc'; 110 | * const a = new AsmdCalc(0.33366666); 111 | * console.log(+a.keepDotLength(6)); 112 | * // => 0.333666 113 | * console.log(+a.keepDotLength(5, false)); 114 | * // => 0.33366 115 | * console.log(+a.keepDotLength(4, true)); 116 | * // => 0.3337 117 | * ``` 118 | * 119 | * @param len 120 | * @param isRounding 121 | */ 122 | public keepDotLength(len: number, isRounding = false) { 123 | this.total = keepDotLength(this.total, len, isRounding); 124 | return this; 125 | } 126 | /** 127 | * 最多保留 N 位小数 128 | * 129 | * ### Example (es module) 130 | * ```js 131 | * import { AsmdCalc } from 'asmd-calc'; 132 | * const a = new AsmdCalc(1.45); 133 | * console.log(a.toFixed(1)); 134 | * // => 0.5 135 | * console.log(a.toFixed(2)); 136 | * // => 1.45 137 | * console.log(a.toFixed(3)); 138 | * // => 1.450 139 | * ``` 140 | * 141 | */ 142 | public toFixed(len: number) { 143 | return toFixed(this.total, len); 144 | } 145 | protected valueOf() { 146 | return this.total; 147 | } 148 | protected toString() { 149 | return toNonExponential(this.total); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/lib/calculation.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: renxia 3 | * @Date: 2018-09-10 15:10:40 4 | * @LastEditors: renxia 5 | * @LastEditTime: 2025-04-02 16:47:52 6 | * @Description: 支持浮点数精度的加减乘除四则运算 7 | */ 8 | 9 | import { getDecimalLen, isNull, toNonExponential } from './utils'; 10 | 11 | /** 12 | * addition 加法计算。与普通加法运算不同的是,任意参数为 `null/NaN/undefined`,均会被视为 `0` 而忽略 13 | * 14 | * ### Example (es module) 15 | * ```js 16 | * import { add } from 'asmd-calc'; 17 | * console.log(add(0.1, 0.2, 3)); 18 | * // => 3.3 19 | * ``` 20 | * 21 | * @returns 永远返回有效的数值,不存在 NaN(视作0处理) 22 | */ 23 | export function add(...args): number { 24 | let total = Number(args[0]); 25 | 26 | args.slice(1).forEach((value) => { 27 | if (!value) return; 28 | if (!total) return (total = value); 29 | 30 | const tDLen = getDecimalLen(total); 31 | const vDLen = getDecimalLen(value); 32 | const decimalLen = tDLen + vDLen; 33 | 34 | if (decimalLen) { 35 | const e = Math.pow(10, Math.max(tDLen, vDLen)); 36 | total = (Math.round(total * e) + Math.round(value * e)) / e; 37 | } else { 38 | total += typeof value === 'number' ? value : Number(value) || 0; 39 | } 40 | }); 41 | 42 | return total; 43 | } 44 | 45 | /** 46 | * subtraction 减法计算。与普通减法运算不同的是,任意参数为 `null/NaN/undefined`,均会被视为 `0` 而忽略 47 | * 48 | * ### Example (es module) 49 | * ```js 50 | * import { sub } from 'asmd-calc'; 51 | * console.log(sub(0.3, 0.2, 0.1)); 52 | * // => 0 53 | * ``` 54 | * 55 | * @param args 第一个值为减数,后续值均为被减数 56 | * @returns 永远返回有效的数值,不存在 NaN(视作0处理) 57 | */ 58 | export function sub(...args): number { 59 | if (!args.length) return null; 60 | args.forEach((value, idx) => { 61 | if (idx && value) args[idx] = -value; 62 | }); 63 | 64 | return add(...args); 65 | } 66 | 67 | /** 68 | * multiplication 乘法计算。 69 | * - 注意小数位不宜过长(总长度不超过18位,结果不超过18位) 70 | * - 任意参数为 `null/NaN/undefined`,均会被视为 `0` 71 | * 72 | * ### Example (es module) 73 | * ```js 74 | * import { mul } from 'asmd-calc'; 75 | * console.log(mul(3, 0.2, 0.1)); 76 | * // => 0.06 77 | * ``` 78 | * 79 | * @returns 永远返回有效的数值,不存在 NaN(视作0处理) 80 | */ 81 | export function mul(...args): number { 82 | let total = Number(args[0]); 83 | 84 | if (!args.length) return 0; 85 | 86 | args.slice(1).forEach((value) => { 87 | if (typeof value !== 'number') value = +value || 0; 88 | 89 | if (!value || !total) return (total = 0); 90 | if (1 === total) return (total = value); 91 | 92 | const tDLen = getDecimalLen(total); 93 | const vDLen = getDecimalLen(value); 94 | const decimalLen = tDLen + vDLen; 95 | 96 | if (decimalLen) { 97 | const e = Math.pow(10, decimalLen); 98 | total = tDLen ? Math.round(total * Math.pow(10, tDLen)) : total; // Number(toNonExponential(total).replace('.', '')); 99 | value = vDLen ? Math.round(value * Math.pow(10, vDLen)) : value; // Number(toNonExponential(value).replace('.', '')); 100 | total = (total * value) / e; 101 | } else { 102 | total *= value; 103 | } 104 | }); 105 | 106 | return total; 107 | } 108 | /** 109 | * division 除法计算。 110 | * 111 | * 任意参数为 `null/NaN/undefined`,均会被视为 `0`。于是会有如下情况: 112 | * ``` 113 | * NaN / 1 = 0 / 1 = 0 114 | * 0 / NaN = 0 / 0 = NaN 115 | * 1 / NaN = 1 / 0 = Infinity 116 | * ``` 117 | * 118 | * ### Example (es module) 119 | * ```js 120 | * import { div } from 'asmd-calc'; 121 | * console.log(div(0.6, 0.1, 0.2)); 122 | * // => 30 123 | * ``` 124 | * 125 | * @param args 第一个参数为除数,后续参数均为被除数 126 | * @returns 永远返回有效的数值。 127 | */ 128 | export function div(...args): number { 129 | if (!args.length) return null; 130 | 131 | let total: number = args[0]; 132 | 133 | args.slice(1).forEach((value) => { 134 | if (!value || !total) { 135 | // null \ NaN \ undefined 136 | total /= value; 137 | return; 138 | } 139 | 140 | value = Number(value); 141 | 142 | const tDLen = getDecimalLen(total); 143 | const vDLen = getDecimalLen(value); 144 | const decimalLen = vDLen - tDLen; 145 | 146 | if (vDLen || tDLen) { 147 | let e = Math.pow(10, decimalLen); 148 | if (decimalLen < 0) e = Number(e.toFixed(-decimalLen)); 149 | 150 | total = tDLen ? Math.round(total * Math.pow(10, tDLen)) : total; // Number(toNonExponential(total).replace('.', '')); 151 | value = vDLen ? Math.round(value * Math.pow(10, vDLen)) : value; // Number(toNonExponential(value).replace('.', '')); 152 | 153 | total = mul(total / value, e); 154 | } else { 155 | total /= value; 156 | } 157 | }); 158 | 159 | return total; 160 | } 161 | /** 162 | * 最多保留 N 位小数 163 | * 164 | * ### Example (es module) 165 | * ```js 166 | * import { keepDotDLength } from 'asmd-calc'; 167 | * console.log(keepDotDLength(0.66666, 2)); 168 | * // => 0.66 169 | * console.log(keepDotDLength(0.66666, 2, false)); 170 | * // => 0.66 171 | * console.log(keepDotDLength(0.66666, 2, true)); 172 | * // => 0.67 173 | * ``` 174 | * 175 | * @param value 数值 176 | * @param precision 小数位数,应为 0-16 之间的整数 177 | * @param isrounding 是否四舍五入取值。默认 false 178 | */ 179 | export function keepDotLength( 180 | value: number | string, 181 | precision: number, 182 | type: 'ceil' | 'round' | 'fround' | 'floor' | boolean = 'floor', 183 | ): number | null { 184 | if (isNull(value)) return null; 185 | 186 | if (type === true) type = 'round'; 187 | else if (!type) type = 'floor'; 188 | 189 | return Number(toFixed(value, precision, type)); 190 | } 191 | 192 | /** 193 | * toFixed 方法重写 【解决 Number.toFixed 方法在不同浏览器表现不一致的问题】 194 | * 195 | * ### Example (es module) 196 | * ```js 197 | * import { toFixed } from 'asmd-calc'; 198 | * console.log(toFixed(0.66666, 2)); 199 | * // => 0.67 200 | * console.log(toFixed(1.45, 1)); 201 | * // => 1.5 202 | * console.log(toFixed(1.55, 1)); 203 | * // => 1.6 204 | * console.log(toFixed(1.515, 2)); 205 | * // => 1.52 206 | * ``` 207 | * 208 | * @param value 数值 209 | * @param precision 小数位数,应为 0-16 之间的整数 210 | * @param type 进位处理方法: 211 | * - round - 四舍五入 212 | * - ceil 向上取整 213 | * - floor 向下取整(截断) 214 | */ 215 | export function toFixed(value: number | string, precision: number, type: 'ceil' | 'round' | 'fround' | 'floor' = 'round'): string | null { 216 | if (isNull(value)) return null; 217 | if (!(type in Math)) throw new Error('type must be one of ["ceil", "round", "fround", "floor"]. current: ' + type); 218 | 219 | value = Number(value); 220 | precision = Math.max(Number(precision), 0); 221 | if (!precision) return String(Math[type](value)); 222 | 223 | const valList = toNonExponential(value).split('.'); 224 | 225 | if (!valList[1]) { 226 | valList[1] = new Array(precision + 1).join('0'); 227 | return valList.join('.'); 228 | } else { 229 | const result = String(Math[type](Math.pow(10, precision) * Number('0.' + valList[1])) / Math.pow(10, precision)).split('.'); 230 | 231 | // result = 1,则没有小数部分 232 | if (!result[1]) { 233 | result[1] = ''; 234 | valList[0] = String(Number(valList[0]) + Number(result[0])); 235 | } 236 | 237 | // 小数部分末尾补 0 238 | if (result[1].length < precision) { 239 | result[1] += new Array(precision - result[1].length + 1).join('0'); 240 | } 241 | 242 | valList[1] = result[1]; 243 | return valList.join('.'); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取指定数值的小数位长度 3 | * @param num 4 | */ 5 | export function getDecimalLen(num): number { 6 | if (!isDecimal(num)) return 0; 7 | try { 8 | return toNonExponential(num).split('.')[1].length; 9 | } catch (e) { 10 | console.error(num, e); 11 | return 0; 12 | } 13 | } 14 | 15 | /** 16 | * 是否为小数 17 | * @param n 要识别的参数 18 | * @param useRegExp 是否使用正则方式。默认为 false 19 | */ 20 | export function isDecimal(value: number | string): boolean { 21 | const n = Number(value); 22 | return !Number.isNaN(n) && Math.ceil(n) !== n; 23 | } 24 | 25 | /** 26 | * NaN、null、undefined 返回 true,其它为 false 27 | * @param value 28 | */ 29 | export function isNull(value): boolean { 30 | return null == value || isNaN(value); 31 | } 32 | 33 | /** 将指定的浮点数转换为非科学计数法的字符串格式 */ 34 | export function toNonExponential(num: number): string { 35 | if (!num) return '0'; 36 | const strNum = String(num); 37 | if (strNum.indexOf('e') === -1) return strNum; 38 | num = Number(num); 39 | const m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/); 40 | return num.toFixed(Math.max(0, (m[1] || '').length - Number(m[2]))); 41 | } 42 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "build/main", 6 | "target": "es5", 7 | "module": "commonjs" 8 | }, 9 | "include": ["src/**/*.ts"], 10 | "exclude": ["node_modules/**", "__test__", "src/**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build/module", 5 | "target": "esnext", 6 | "module": "esnext" 7 | }, 8 | "include": ["**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "outDir": "build/module", 5 | "moduleResolution": "node", 6 | "inlineSourceMap": false, 7 | "esModuleInterop": true, 8 | "noUnusedLocals": true, 9 | "noUnusedParameters": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | 14 | /* Debugging Options */ 15 | "traceResolution": false, 16 | "listEmittedFiles": false, 17 | "listFiles": false, 18 | "pretty": true, 19 | 20 | "lib": ["DOM", "esnext"], 21 | "typeRoots": ["./node_modules/@types"] 22 | }, 23 | "exclude": ["node_modules/**"], 24 | "include": ["**/*.ts"], 25 | "compileOnSave": false 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.module.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "build/module", 6 | "target": "esnext", 7 | "module": "esnext" 8 | }, 9 | "include": ["src/**/*.ts"], 10 | "exclude": ["node_modules/**", "__test__", "src/**/*.spec.ts"] 11 | } 12 | --------------------------------------------------------------------------------