1
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019-present react-component
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# @rc-component/mini-decimal
|
||||
|
||||
A mini decimal calculator which only support `add`, `multi` or compare operation for mini bundle size.
|
||||
|
||||
[![NPM version][npm-image]][npm-url] [](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [![Codecov][codecov-image]][codecov-url] [![npm download][download-image]][download-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/@rc-component/mini-decimal.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/@rc-component/mini-decimal
|
||||
[github-actions-image]: https://github.com/react-component/mini-decimal/workflows/CI/badge.svg
|
||||
[github-actions-url]: https://github.com/react-component/mini-decimal/actions
|
||||
[codecov-image]: https://img.shields.io/codecov/c/github/react-component/mini-decimal/master.svg?style=flat-square
|
||||
[codecov-url]: https://codecov.io/gh/react-component/mini-decimal/branch/master
|
||||
[download-image]: https://img.shields.io/npm/dm/@rc-component/mini-decimal.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/@rc-component/mini-decimal
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm test
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import getMiniDecimal from '@rc-component/mini-decimal';
|
||||
|
||||
// Add
|
||||
getMiniDecimal('0.1').add('0.2').toString(); // 0.3
|
||||
|
||||
// Multi
|
||||
getMiniDecimal('0.1').multi('0.2').toString(); // 0.02
|
||||
|
||||
// Negate
|
||||
getMiniDecimal('0.1').negate().toString(); // -0.1
|
||||
|
||||
// Equal
|
||||
getMiniDecimal('0.1').equal('0.1'); // true
|
||||
getMiniDecimal('0.1').equal('0.2'); // false
|
||||
|
||||
// Less Equals
|
||||
getMiniDecimal('0.1').lessEquals('0.2'); // true
|
||||
getMiniDecimal('0.1').lessEquals('0.1'); // false
|
||||
```
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import type { DecimalClass, ValueType } from './interface';
|
||||
export default class BigIntDecimal implements DecimalClass {
|
||||
origin: string;
|
||||
negative: boolean;
|
||||
integer: bigint;
|
||||
decimal: bigint;
|
||||
/** BigInt will convert `0009` to `9`. We need record the len of decimal */
|
||||
decimalLen: number;
|
||||
empty: boolean;
|
||||
nan: boolean;
|
||||
constructor(value: string | number);
|
||||
private getMark;
|
||||
private getIntegerStr;
|
||||
/**
|
||||
* @private get decimal string
|
||||
*/
|
||||
getDecimalStr(): string;
|
||||
/**
|
||||
* @private Align BigIntDecimal with same decimal length. e.g. 12.3 + 5 = 1230000
|
||||
* This is used for add function only.
|
||||
*/
|
||||
alignDecimal(decimalLength: number): bigint;
|
||||
negate(): BigIntDecimal;
|
||||
private cal;
|
||||
add(value: ValueType): BigIntDecimal;
|
||||
multi(value: ValueType): BigIntDecimal;
|
||||
isEmpty(): boolean;
|
||||
isNaN(): boolean;
|
||||
isInvalidate(): boolean;
|
||||
equals(target: DecimalClass): boolean;
|
||||
lessEquals(target: DecimalClass): boolean;
|
||||
toNumber(): number;
|
||||
toString(safe?: boolean): string;
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/esm/createClass";
|
||||
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
||||
import { isE, isEmpty, num2str, trimNumber, validateNumber } from "./numberUtil";
|
||||
var BigIntDecimal = /*#__PURE__*/function () {
|
||||
function BigIntDecimal(value) {
|
||||
_classCallCheck(this, BigIntDecimal);
|
||||
_defineProperty(this, "origin", '');
|
||||
_defineProperty(this, "negative", void 0);
|
||||
_defineProperty(this, "integer", void 0);
|
||||
_defineProperty(this, "decimal", void 0);
|
||||
/** BigInt will convert `0009` to `9`. We need record the len of decimal */
|
||||
_defineProperty(this, "decimalLen", void 0);
|
||||
_defineProperty(this, "empty", void 0);
|
||||
_defineProperty(this, "nan", void 0);
|
||||
if (isEmpty(value)) {
|
||||
this.empty = true;
|
||||
return;
|
||||
}
|
||||
this.origin = String(value);
|
||||
|
||||
// Act like Number convert
|
||||
if (value === '-' || Number.isNaN(value)) {
|
||||
this.nan = true;
|
||||
return;
|
||||
}
|
||||
var mergedValue = value;
|
||||
|
||||
// We need convert back to Number since it require `toFixed` to handle this
|
||||
if (isE(mergedValue)) {
|
||||
mergedValue = Number(mergedValue);
|
||||
}
|
||||
mergedValue = typeof mergedValue === 'string' ? mergedValue : num2str(mergedValue);
|
||||
if (validateNumber(mergedValue)) {
|
||||
var trimRet = trimNumber(mergedValue);
|
||||
this.negative = trimRet.negative;
|
||||
var numbers = trimRet.trimStr.split('.');
|
||||
this.integer = BigInt(numbers[0]);
|
||||
var decimalStr = numbers[1] || '0';
|
||||
this.decimal = BigInt(decimalStr);
|
||||
this.decimalLen = decimalStr.length;
|
||||
} else {
|
||||
this.nan = true;
|
||||
}
|
||||
}
|
||||
_createClass(BigIntDecimal, [{
|
||||
key: "getMark",
|
||||
value: function getMark() {
|
||||
return this.negative ? '-' : '';
|
||||
}
|
||||
}, {
|
||||
key: "getIntegerStr",
|
||||
value: function getIntegerStr() {
|
||||
return this.integer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private get decimal string
|
||||
*/
|
||||
}, {
|
||||
key: "getDecimalStr",
|
||||
value: function getDecimalStr() {
|
||||
return this.decimal.toString().padStart(this.decimalLen, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private Align BigIntDecimal with same decimal length. e.g. 12.3 + 5 = 1230000
|
||||
* This is used for add function only.
|
||||
*/
|
||||
}, {
|
||||
key: "alignDecimal",
|
||||
value: function alignDecimal(decimalLength) {
|
||||
var str = "".concat(this.getMark()).concat(this.getIntegerStr()).concat(this.getDecimalStr().padEnd(decimalLength, '0'));
|
||||
return BigInt(str);
|
||||
}
|
||||
}, {
|
||||
key: "negate",
|
||||
value: function negate() {
|
||||
var clone = new BigIntDecimal(this.toString());
|
||||
clone.negative = !clone.negative;
|
||||
return clone;
|
||||
}
|
||||
}, {
|
||||
key: "cal",
|
||||
value: function cal(offset, calculator, calDecimalLen) {
|
||||
var maxDecimalLength = Math.max(this.getDecimalStr().length, offset.getDecimalStr().length);
|
||||
var myAlignedDecimal = this.alignDecimal(maxDecimalLength);
|
||||
var offsetAlignedDecimal = offset.alignDecimal(maxDecimalLength);
|
||||
var valueStr = calculator(myAlignedDecimal, offsetAlignedDecimal).toString();
|
||||
var nextDecimalLength = calDecimalLen(maxDecimalLength);
|
||||
|
||||
// We need fill string length back to `maxDecimalLength` to avoid parser failed
|
||||
var _trimNumber = trimNumber(valueStr),
|
||||
negativeStr = _trimNumber.negativeStr,
|
||||
trimStr = _trimNumber.trimStr;
|
||||
var hydrateValueStr = "".concat(negativeStr).concat(trimStr.padStart(nextDecimalLength + 1, '0'));
|
||||
return new BigIntDecimal("".concat(hydrateValueStr.slice(0, -nextDecimalLength), ".").concat(hydrateValueStr.slice(-nextDecimalLength)));
|
||||
}
|
||||
}, {
|
||||
key: "add",
|
||||
value: function add(value) {
|
||||
if (this.isInvalidate()) {
|
||||
return new BigIntDecimal(value);
|
||||
}
|
||||
var offset = new BigIntDecimal(value);
|
||||
if (offset.isInvalidate()) {
|
||||
return this;
|
||||
}
|
||||
return this.cal(offset, function (num1, num2) {
|
||||
return num1 + num2;
|
||||
}, function (len) {
|
||||
return len;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "multi",
|
||||
value: function multi(value) {
|
||||
var target = new BigIntDecimal(value);
|
||||
if (this.isInvalidate() || target.isInvalidate()) {
|
||||
return new BigIntDecimal(NaN);
|
||||
}
|
||||
return this.cal(target, function (num1, num2) {
|
||||
return num1 * num2;
|
||||
}, function (len) {
|
||||
return len * 2;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "isEmpty",
|
||||
value: function isEmpty() {
|
||||
return this.empty;
|
||||
}
|
||||
}, {
|
||||
key: "isNaN",
|
||||
value: function isNaN() {
|
||||
return this.nan;
|
||||
}
|
||||
}, {
|
||||
key: "isInvalidate",
|
||||
value: function isInvalidate() {
|
||||
return this.isEmpty() || this.isNaN();
|
||||
}
|
||||
}, {
|
||||
key: "equals",
|
||||
value: function equals(target) {
|
||||
return this.toString() === (target === null || target === void 0 ? void 0 : target.toString());
|
||||
}
|
||||
}, {
|
||||
key: "lessEquals",
|
||||
value: function lessEquals(target) {
|
||||
return this.add(target.negate().toString()).toNumber() <= 0;
|
||||
}
|
||||
}, {
|
||||
key: "toNumber",
|
||||
value: function toNumber() {
|
||||
if (this.isNaN()) {
|
||||
return NaN;
|
||||
}
|
||||
return Number(this.toString());
|
||||
}
|
||||
}, {
|
||||
key: "toString",
|
||||
value: function toString() {
|
||||
var safe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
||||
if (!safe) {
|
||||
return this.origin;
|
||||
}
|
||||
if (this.isInvalidate()) {
|
||||
return '';
|
||||
}
|
||||
return trimNumber("".concat(this.getMark()).concat(this.getIntegerStr(), ".").concat(this.getDecimalStr())).fullStr;
|
||||
}
|
||||
}]);
|
||||
return BigIntDecimal;
|
||||
}();
|
||||
export { BigIntDecimal as default };
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import BigIntDecimal from './BigIntDecimal';
|
||||
import NumberDecimal from './NumberDecimal';
|
||||
import type { DecimalClass, ValueType } from './interface';
|
||||
export { NumberDecimal, BigIntDecimal };
|
||||
export type { DecimalClass, ValueType };
|
||||
export default function getMiniDecimal(value: ValueType): DecimalClass;
|
||||
/**
|
||||
* Align the logic of toFixed to around like 1.5 => 2.
|
||||
* If set `cutOnly`, will just remove the over decimal part.
|
||||
*/
|
||||
export declare function toFixed(numStr: string, separatorStr: string, precision?: number, cutOnly?: boolean): string;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import BigIntDecimal from "./BigIntDecimal";
|
||||
import NumberDecimal from "./NumberDecimal";
|
||||
import { trimNumber } from "./numberUtil";
|
||||
import { supportBigInt } from "./supportUtil";
|
||||
|
||||
// Still support origin export
|
||||
export { NumberDecimal, BigIntDecimal };
|
||||
export default function getMiniDecimal(value) {
|
||||
// We use BigInt here.
|
||||
// Will fallback to Number if not support.
|
||||
if (supportBigInt()) {
|
||||
return new BigIntDecimal(value);
|
||||
}
|
||||
return new NumberDecimal(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the logic of toFixed to around like 1.5 => 2.
|
||||
* If set `cutOnly`, will just remove the over decimal part.
|
||||
*/
|
||||
export function toFixed(numStr, separatorStr, precision) {
|
||||
var cutOnly = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
||||
if (numStr === '') {
|
||||
return '';
|
||||
}
|
||||
var _trimNumber = trimNumber(numStr),
|
||||
negativeStr = _trimNumber.negativeStr,
|
||||
integerStr = _trimNumber.integerStr,
|
||||
decimalStr = _trimNumber.decimalStr;
|
||||
var precisionDecimalStr = "".concat(separatorStr).concat(decimalStr);
|
||||
var numberWithoutDecimal = "".concat(negativeStr).concat(integerStr);
|
||||
if (precision >= 0) {
|
||||
// We will get last + 1 number to check if need advanced number
|
||||
var advancedNum = Number(decimalStr[precision]);
|
||||
if (advancedNum >= 5 && !cutOnly) {
|
||||
var advancedDecimal = getMiniDecimal(numStr).add("".concat(negativeStr, "0.").concat('0'.repeat(precision)).concat(10 - advancedNum));
|
||||
return toFixed(advancedDecimal.toString(), separatorStr, precision, cutOnly);
|
||||
}
|
||||
if (precision === 0) {
|
||||
return numberWithoutDecimal;
|
||||
}
|
||||
return "".concat(numberWithoutDecimal).concat(separatorStr).concat(decimalStr.padEnd(precision, '0').slice(0, precision));
|
||||
}
|
||||
if (precisionDecimalStr === '.0') {
|
||||
return numberWithoutDecimal;
|
||||
}
|
||||
return "".concat(numberWithoutDecimal).concat(precisionDecimalStr);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import type { DecimalClass, ValueType } from './interface';
|
||||
/**
|
||||
* We can remove this when IE not support anymore
|
||||
*/
|
||||
export default class NumberDecimal implements DecimalClass {
|
||||
origin: string;
|
||||
number: number;
|
||||
empty: boolean;
|
||||
constructor(value: ValueType);
|
||||
negate(): NumberDecimal;
|
||||
add(value: ValueType): NumberDecimal;
|
||||
multi(value: ValueType): NumberDecimal;
|
||||
isEmpty(): boolean;
|
||||
isNaN(): boolean;
|
||||
isInvalidate(): boolean;
|
||||
equals(target: DecimalClass): boolean;
|
||||
lessEquals(target: DecimalClass): boolean;
|
||||
toNumber(): number;
|
||||
toString(safe?: boolean): string;
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/esm/createClass";
|
||||
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
||||
import { getNumberPrecision, isE, isEmpty, num2str } from "./numberUtil";
|
||||
|
||||
/**
|
||||
* We can remove this when IE not support anymore
|
||||
*/
|
||||
var NumberDecimal = /*#__PURE__*/function () {
|
||||
function NumberDecimal(value) {
|
||||
_classCallCheck(this, NumberDecimal);
|
||||
_defineProperty(this, "origin", '');
|
||||
_defineProperty(this, "number", void 0);
|
||||
_defineProperty(this, "empty", void 0);
|
||||
if (isEmpty(value)) {
|
||||
this.empty = true;
|
||||
return;
|
||||
}
|
||||
this.origin = String(value);
|
||||
this.number = Number(value);
|
||||
}
|
||||
_createClass(NumberDecimal, [{
|
||||
key: "negate",
|
||||
value: function negate() {
|
||||
return new NumberDecimal(-this.toNumber());
|
||||
}
|
||||
}, {
|
||||
key: "add",
|
||||
value: function add(value) {
|
||||
if (this.isInvalidate()) {
|
||||
return new NumberDecimal(value);
|
||||
}
|
||||
var target = Number(value);
|
||||
if (Number.isNaN(target)) {
|
||||
return this;
|
||||
}
|
||||
var number = this.number + target;
|
||||
|
||||
// [Legacy] Back to safe integer
|
||||
if (number > Number.MAX_SAFE_INTEGER) {
|
||||
return new NumberDecimal(Number.MAX_SAFE_INTEGER);
|
||||
}
|
||||
if (number < Number.MIN_SAFE_INTEGER) {
|
||||
return new NumberDecimal(Number.MIN_SAFE_INTEGER);
|
||||
}
|
||||
var maxPrecision = Math.max(getNumberPrecision(this.number), getNumberPrecision(target));
|
||||
return new NumberDecimal(number.toFixed(maxPrecision));
|
||||
}
|
||||
}, {
|
||||
key: "multi",
|
||||
value: function multi(value) {
|
||||
var target = Number(value);
|
||||
if (this.isInvalidate() || Number.isNaN(target)) {
|
||||
return new NumberDecimal(NaN);
|
||||
}
|
||||
var number = this.number * target;
|
||||
|
||||
// [Legacy] Back to safe integer
|
||||
if (number > Number.MAX_SAFE_INTEGER) {
|
||||
return new NumberDecimal(Number.MAX_SAFE_INTEGER);
|
||||
}
|
||||
if (number < Number.MIN_SAFE_INTEGER) {
|
||||
return new NumberDecimal(Number.MIN_SAFE_INTEGER);
|
||||
}
|
||||
var maxPrecision = Math.max(getNumberPrecision(this.number), getNumberPrecision(target));
|
||||
return new NumberDecimal(number.toFixed(maxPrecision));
|
||||
}
|
||||
}, {
|
||||
key: "isEmpty",
|
||||
value: function isEmpty() {
|
||||
return this.empty;
|
||||
}
|
||||
}, {
|
||||
key: "isNaN",
|
||||
value: function isNaN() {
|
||||
return Number.isNaN(this.number);
|
||||
}
|
||||
}, {
|
||||
key: "isInvalidate",
|
||||
value: function isInvalidate() {
|
||||
return this.isEmpty() || this.isNaN();
|
||||
}
|
||||
}, {
|
||||
key: "equals",
|
||||
value: function equals(target) {
|
||||
return this.toNumber() === (target === null || target === void 0 ? void 0 : target.toNumber());
|
||||
}
|
||||
}, {
|
||||
key: "lessEquals",
|
||||
value: function lessEquals(target) {
|
||||
return this.add(target.negate().toString()).toNumber() <= 0;
|
||||
}
|
||||
}, {
|
||||
key: "toNumber",
|
||||
value: function toNumber() {
|
||||
return this.number;
|
||||
}
|
||||
}, {
|
||||
key: "toString",
|
||||
value: function toString() {
|
||||
var safe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
||||
if (!safe) {
|
||||
return this.origin;
|
||||
}
|
||||
if (this.isInvalidate()) {
|
||||
return '';
|
||||
}
|
||||
if (isE(this.number) && getNumberPrecision(this.number) > 100) {
|
||||
return String(this.number);
|
||||
}
|
||||
return num2str(this.number);
|
||||
}
|
||||
}]);
|
||||
return NumberDecimal;
|
||||
}();
|
||||
export { NumberDecimal as default };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import getMiniDecimal from './MiniDecimal';
|
||||
export * from './MiniDecimal';
|
||||
import { trimNumber, getNumberPrecision, num2str, validateNumber } from './numberUtil';
|
||||
export { trimNumber, getNumberPrecision, num2str, validateNumber };
|
||||
export default getMiniDecimal;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import getMiniDecimal from "./MiniDecimal";
|
||||
export * from "./MiniDecimal";
|
||||
import { trimNumber, getNumberPrecision, num2str, validateNumber } from "./numberUtil";
|
||||
export { trimNumber, getNumberPrecision, num2str, validateNumber };
|
||||
export default getMiniDecimal;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
export type ValueType = string | number;
|
||||
export interface DecimalClass {
|
||||
add: (value: ValueType) => DecimalClass;
|
||||
multi: (value: ValueType) => DecimalClass;
|
||||
isEmpty: () => boolean;
|
||||
isNaN: () => boolean;
|
||||
isInvalidate: () => boolean;
|
||||
toNumber: () => number;
|
||||
/**
|
||||
* Parse value as string. Will return empty string if `isInvalidate`.
|
||||
* You can set `safe=false` to get origin string content.
|
||||
*/
|
||||
toString: (safe?: boolean) => string;
|
||||
equals: (target: DecimalClass) => boolean;
|
||||
lessEquals: (target: DecimalClass) => boolean;
|
||||
negate: () => DecimalClass;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import type { ValueType } from './interface';
|
||||
export declare function isEmpty(value: ValueType): boolean;
|
||||
/**
|
||||
* Format string number to readable number
|
||||
*/
|
||||
export declare function trimNumber(numStr: string): {
|
||||
negative: boolean;
|
||||
negativeStr: string;
|
||||
trimStr: string;
|
||||
integerStr: string;
|
||||
decimalStr: string;
|
||||
fullStr: string;
|
||||
};
|
||||
export declare function isE(number: string | number): boolean;
|
||||
/**
|
||||
* [Legacy] Convert 1e-9 to 0.000000001.
|
||||
* This may lose some precision if user really want 1e-9.
|
||||
*/
|
||||
export declare function getNumberPrecision(number: string | number): number;
|
||||
/**
|
||||
* Convert number (includes scientific notation) to -xxx.yyy format
|
||||
*/
|
||||
export declare function num2str(number: number): string;
|
||||
export declare function validateNumber(num: string | number): boolean;
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
||||
import { supportBigInt } from "./supportUtil";
|
||||
export function isEmpty(value) {
|
||||
return !value && value !== 0 && !Number.isNaN(value) || !String(value).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format string number to readable number
|
||||
*/
|
||||
export function trimNumber(numStr) {
|
||||
var str = numStr.trim();
|
||||
var negative = str.startsWith('-');
|
||||
if (negative) {
|
||||
str = str.slice(1);
|
||||
}
|
||||
str = str
|
||||
// Remove decimal 0. `1.000` => `1.`, `1.100` => `1.1`
|
||||
.replace(/(\.\d*[^0])0*$/, '$1')
|
||||
// Remove useless decimal. `1.` => `1`
|
||||
.replace(/\.0*$/, '')
|
||||
// Remove integer 0. `0001` => `1`, 000.1' => `.1`
|
||||
.replace(/^0+/, '');
|
||||
if (str.startsWith('.')) {
|
||||
str = "0".concat(str);
|
||||
}
|
||||
var trimStr = str || '0';
|
||||
var splitNumber = trimStr.split('.');
|
||||
var integerStr = splitNumber[0] || '0';
|
||||
var decimalStr = splitNumber[1] || '0';
|
||||
if (integerStr === '0' && decimalStr === '0') {
|
||||
negative = false;
|
||||
}
|
||||
var negativeStr = negative ? '-' : '';
|
||||
return {
|
||||
negative: negative,
|
||||
negativeStr: negativeStr,
|
||||
trimStr: trimStr,
|
||||
integerStr: integerStr,
|
||||
decimalStr: decimalStr,
|
||||
fullStr: "".concat(negativeStr).concat(trimStr)
|
||||
};
|
||||
}
|
||||
export function isE(number) {
|
||||
var str = String(number);
|
||||
return !Number.isNaN(Number(str)) && str.includes('e');
|
||||
}
|
||||
/**
|
||||
* Parse a scientific-notation string into reusable parts.
|
||||
*
|
||||
* The idea is to split the value into mantissa and exponent first, then
|
||||
* normalize the mantissa into sign, integer/decimal segments, and a compact
|
||||
* digit sequence so later logic can move the decimal point without re-parsing.
|
||||
*/
|
||||
function parseScientificNotation(numStr) {
|
||||
var _numStr$toLowerCase$s = numStr.toLowerCase().split('e'),
|
||||
_numStr$toLowerCase$s2 = _slicedToArray(_numStr$toLowerCase$s, 2),
|
||||
mantissa = _numStr$toLowerCase$s2[0],
|
||||
_numStr$toLowerCase$s3 = _numStr$toLowerCase$s2[1],
|
||||
exponent = _numStr$toLowerCase$s3 === void 0 ? '0' : _numStr$toLowerCase$s3;
|
||||
var negative = mantissa.startsWith('-');
|
||||
var unsignedMantissa = negative ? mantissa.slice(1) : mantissa;
|
||||
var _unsignedMantissa$spl = unsignedMantissa.split('.'),
|
||||
_unsignedMantissa$spl2 = _slicedToArray(_unsignedMantissa$spl, 2),
|
||||
_unsignedMantissa$spl3 = _unsignedMantissa$spl2[0],
|
||||
integer = _unsignedMantissa$spl3 === void 0 ? '0' : _unsignedMantissa$spl3,
|
||||
_unsignedMantissa$spl4 = _unsignedMantissa$spl2[1],
|
||||
decimal = _unsignedMantissa$spl4 === void 0 ? '' : _unsignedMantissa$spl4;
|
||||
var digits = "".concat(integer).concat(decimal).replace(/^0+/, '') || '0';
|
||||
return {
|
||||
decimal: decimal,
|
||||
digits: digits,
|
||||
exponent: Number(exponent),
|
||||
integer: integer,
|
||||
negative: negative
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand parsed scientific notation into a plain decimal string.
|
||||
*
|
||||
* The core idea is to calculate where the decimal point lands after applying
|
||||
* the exponent, then rebuild the string by either padding zeros or inserting
|
||||
* the decimal point inside the normalized digit sequence.
|
||||
*/
|
||||
function expandScientificNotation(parsed) {
|
||||
var decimal = parsed.decimal,
|
||||
digits = parsed.digits,
|
||||
exponent = parsed.exponent,
|
||||
integer = parsed.integer,
|
||||
negative = parsed.negative;
|
||||
if (digits === '0') {
|
||||
return '0';
|
||||
}
|
||||
var integerDigits = integer.replace(/^0+/, '').length;
|
||||
var leadingDecimalZeros = (decimal.match(/^0*/) || [''])[0].length;
|
||||
var initialDecimalIndex = integerDigits || -leadingDecimalZeros;
|
||||
var decimalIndex = initialDecimalIndex + exponent;
|
||||
var expanded = '';
|
||||
if (decimalIndex <= 0) {
|
||||
expanded = "0.".concat('0'.repeat(-decimalIndex)).concat(digits);
|
||||
} else if (decimalIndex >= digits.length) {
|
||||
expanded = "".concat(digits).concat('0'.repeat(decimalIndex - digits.length));
|
||||
} else {
|
||||
expanded = "".concat(digits.slice(0, decimalIndex), ".").concat(digits.slice(decimalIndex));
|
||||
}
|
||||
return "".concat(negative ? '-' : '').concat(expanded);
|
||||
}
|
||||
function getScientificPrecision(parsed) {
|
||||
if (parsed.exponent >= 0) {
|
||||
return Math.max(0, parsed.decimal.length - parsed.exponent);
|
||||
}
|
||||
return Math.abs(parsed.exponent) + parsed.decimal.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Legacy] Convert 1e-9 to 0.000000001.
|
||||
* This may lose some precision if user really want 1e-9.
|
||||
*/
|
||||
export function getNumberPrecision(number) {
|
||||
var numStr = String(number);
|
||||
if (isE(number)) {
|
||||
return getScientificPrecision(parseScientificNotation(numStr));
|
||||
}
|
||||
return numStr.includes('.') && validateNumber(numStr) ? numStr.length - numStr.indexOf('.') - 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert number (includes scientific notation) to -xxx.yyy format
|
||||
*/
|
||||
export function num2str(number) {
|
||||
var numStr = String(number);
|
||||
if (isE(number)) {
|
||||
if (number > Number.MAX_SAFE_INTEGER) {
|
||||
return String(supportBigInt() ? BigInt(number).toString() : Number.MAX_SAFE_INTEGER);
|
||||
}
|
||||
if (number < Number.MIN_SAFE_INTEGER) {
|
||||
return String(supportBigInt() ? BigInt(number).toString() : Number.MIN_SAFE_INTEGER);
|
||||
}
|
||||
var parsed = parseScientificNotation(numStr);
|
||||
var precision = getScientificPrecision(parsed);
|
||||
numStr = precision > 100 ? expandScientificNotation(parsed) : number.toFixed(precision);
|
||||
}
|
||||
return trimNumber(numStr).fullStr;
|
||||
}
|
||||
export function validateNumber(num) {
|
||||
if (typeof num === 'number') {
|
||||
return !Number.isNaN(num);
|
||||
}
|
||||
|
||||
// Empty
|
||||
if (!num) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
// Normal type: 11.28
|
||||
/^\s*-?\d+(\.\d+)?\s*$/.test(num) ||
|
||||
// Pre-number: 1.
|
||||
/^\s*-?\d+\.\s*$/.test(num) ||
|
||||
// Post-number: .1
|
||||
/^\s*-?\.\d+\s*$/.test(num)
|
||||
);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare function supportBigInt(): boolean;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export function supportBigInt() {
|
||||
return typeof BigInt === 'function';
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import type { DecimalClass, ValueType } from './interface';
|
||||
export default class BigIntDecimal implements DecimalClass {
|
||||
origin: string;
|
||||
negative: boolean;
|
||||
integer: bigint;
|
||||
decimal: bigint;
|
||||
/** BigInt will convert `0009` to `9`. We need record the len of decimal */
|
||||
decimalLen: number;
|
||||
empty: boolean;
|
||||
nan: boolean;
|
||||
constructor(value: string | number);
|
||||
private getMark;
|
||||
private getIntegerStr;
|
||||
/**
|
||||
* @private get decimal string
|
||||
*/
|
||||
getDecimalStr(): string;
|
||||
/**
|
||||
* @private Align BigIntDecimal with same decimal length. e.g. 12.3 + 5 = 1230000
|
||||
* This is used for add function only.
|
||||
*/
|
||||
alignDecimal(decimalLength: number): bigint;
|
||||
negate(): BigIntDecimal;
|
||||
private cal;
|
||||
add(value: ValueType): BigIntDecimal;
|
||||
multi(value: ValueType): BigIntDecimal;
|
||||
isEmpty(): boolean;
|
||||
isNaN(): boolean;
|
||||
isInvalidate(): boolean;
|
||||
equals(target: DecimalClass): boolean;
|
||||
lessEquals(target: DecimalClass): boolean;
|
||||
toNumber(): number;
|
||||
toString(safe?: boolean): string;
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
||||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
||||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
||||
var _numberUtil = require("./numberUtil");
|
||||
var BigIntDecimal = exports.default = /*#__PURE__*/function () {
|
||||
function BigIntDecimal(value) {
|
||||
(0, _classCallCheck2.default)(this, BigIntDecimal);
|
||||
(0, _defineProperty2.default)(this, "origin", '');
|
||||
(0, _defineProperty2.default)(this, "negative", void 0);
|
||||
(0, _defineProperty2.default)(this, "integer", void 0);
|
||||
(0, _defineProperty2.default)(this, "decimal", void 0);
|
||||
/** BigInt will convert `0009` to `9`. We need record the len of decimal */
|
||||
(0, _defineProperty2.default)(this, "decimalLen", void 0);
|
||||
(0, _defineProperty2.default)(this, "empty", void 0);
|
||||
(0, _defineProperty2.default)(this, "nan", void 0);
|
||||
if ((0, _numberUtil.isEmpty)(value)) {
|
||||
this.empty = true;
|
||||
return;
|
||||
}
|
||||
this.origin = String(value);
|
||||
|
||||
// Act like Number convert
|
||||
if (value === '-' || Number.isNaN(value)) {
|
||||
this.nan = true;
|
||||
return;
|
||||
}
|
||||
var mergedValue = value;
|
||||
|
||||
// We need convert back to Number since it require `toFixed` to handle this
|
||||
if ((0, _numberUtil.isE)(mergedValue)) {
|
||||
mergedValue = Number(mergedValue);
|
||||
}
|
||||
mergedValue = typeof mergedValue === 'string' ? mergedValue : (0, _numberUtil.num2str)(mergedValue);
|
||||
if ((0, _numberUtil.validateNumber)(mergedValue)) {
|
||||
var trimRet = (0, _numberUtil.trimNumber)(mergedValue);
|
||||
this.negative = trimRet.negative;
|
||||
var numbers = trimRet.trimStr.split('.');
|
||||
this.integer = BigInt(numbers[0]);
|
||||
var decimalStr = numbers[1] || '0';
|
||||
this.decimal = BigInt(decimalStr);
|
||||
this.decimalLen = decimalStr.length;
|
||||
} else {
|
||||
this.nan = true;
|
||||
}
|
||||
}
|
||||
(0, _createClass2.default)(BigIntDecimal, [{
|
||||
key: "getMark",
|
||||
value: function getMark() {
|
||||
return this.negative ? '-' : '';
|
||||
}
|
||||
}, {
|
||||
key: "getIntegerStr",
|
||||
value: function getIntegerStr() {
|
||||
return this.integer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private get decimal string
|
||||
*/
|
||||
}, {
|
||||
key: "getDecimalStr",
|
||||
value: function getDecimalStr() {
|
||||
return this.decimal.toString().padStart(this.decimalLen, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private Align BigIntDecimal with same decimal length. e.g. 12.3 + 5 = 1230000
|
||||
* This is used for add function only.
|
||||
*/
|
||||
}, {
|
||||
key: "alignDecimal",
|
||||
value: function alignDecimal(decimalLength) {
|
||||
var str = "".concat(this.getMark()).concat(this.getIntegerStr()).concat(this.getDecimalStr().padEnd(decimalLength, '0'));
|
||||
return BigInt(str);
|
||||
}
|
||||
}, {
|
||||
key: "negate",
|
||||
value: function negate() {
|
||||
var clone = new BigIntDecimal(this.toString());
|
||||
clone.negative = !clone.negative;
|
||||
return clone;
|
||||
}
|
||||
}, {
|
||||
key: "cal",
|
||||
value: function cal(offset, calculator, calDecimalLen) {
|
||||
var maxDecimalLength = Math.max(this.getDecimalStr().length, offset.getDecimalStr().length);
|
||||
var myAlignedDecimal = this.alignDecimal(maxDecimalLength);
|
||||
var offsetAlignedDecimal = offset.alignDecimal(maxDecimalLength);
|
||||
var valueStr = calculator(myAlignedDecimal, offsetAlignedDecimal).toString();
|
||||
var nextDecimalLength = calDecimalLen(maxDecimalLength);
|
||||
|
||||
// We need fill string length back to `maxDecimalLength` to avoid parser failed
|
||||
var _trimNumber = (0, _numberUtil.trimNumber)(valueStr),
|
||||
negativeStr = _trimNumber.negativeStr,
|
||||
trimStr = _trimNumber.trimStr;
|
||||
var hydrateValueStr = "".concat(negativeStr).concat(trimStr.padStart(nextDecimalLength + 1, '0'));
|
||||
return new BigIntDecimal("".concat(hydrateValueStr.slice(0, -nextDecimalLength), ".").concat(hydrateValueStr.slice(-nextDecimalLength)));
|
||||
}
|
||||
}, {
|
||||
key: "add",
|
||||
value: function add(value) {
|
||||
if (this.isInvalidate()) {
|
||||
return new BigIntDecimal(value);
|
||||
}
|
||||
var offset = new BigIntDecimal(value);
|
||||
if (offset.isInvalidate()) {
|
||||
return this;
|
||||
}
|
||||
return this.cal(offset, function (num1, num2) {
|
||||
return num1 + num2;
|
||||
}, function (len) {
|
||||
return len;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "multi",
|
||||
value: function multi(value) {
|
||||
var target = new BigIntDecimal(value);
|
||||
if (this.isInvalidate() || target.isInvalidate()) {
|
||||
return new BigIntDecimal(NaN);
|
||||
}
|
||||
return this.cal(target, function (num1, num2) {
|
||||
return num1 * num2;
|
||||
}, function (len) {
|
||||
return len * 2;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "isEmpty",
|
||||
value: function isEmpty() {
|
||||
return this.empty;
|
||||
}
|
||||
}, {
|
||||
key: "isNaN",
|
||||
value: function isNaN() {
|
||||
return this.nan;
|
||||
}
|
||||
}, {
|
||||
key: "isInvalidate",
|
||||
value: function isInvalidate() {
|
||||
return this.isEmpty() || this.isNaN();
|
||||
}
|
||||
}, {
|
||||
key: "equals",
|
||||
value: function equals(target) {
|
||||
return this.toString() === (target === null || target === void 0 ? void 0 : target.toString());
|
||||
}
|
||||
}, {
|
||||
key: "lessEquals",
|
||||
value: function lessEquals(target) {
|
||||
return this.add(target.negate().toString()).toNumber() <= 0;
|
||||
}
|
||||
}, {
|
||||
key: "toNumber",
|
||||
value: function toNumber() {
|
||||
if (this.isNaN()) {
|
||||
return NaN;
|
||||
}
|
||||
return Number(this.toString());
|
||||
}
|
||||
}, {
|
||||
key: "toString",
|
||||
value: function toString() {
|
||||
var safe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
||||
if (!safe) {
|
||||
return this.origin;
|
||||
}
|
||||
if (this.isInvalidate()) {
|
||||
return '';
|
||||
}
|
||||
return (0, _numberUtil.trimNumber)("".concat(this.getMark()).concat(this.getIntegerStr(), ".").concat(this.getDecimalStr())).fullStr;
|
||||
}
|
||||
}]);
|
||||
return BigIntDecimal;
|
||||
}();
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import BigIntDecimal from './BigIntDecimal';
|
||||
import NumberDecimal from './NumberDecimal';
|
||||
import type { DecimalClass, ValueType } from './interface';
|
||||
export { NumberDecimal, BigIntDecimal };
|
||||
export type { DecimalClass, ValueType };
|
||||
export default function getMiniDecimal(value: ValueType): DecimalClass;
|
||||
/**
|
||||
* Align the logic of toFixed to around like 1.5 => 2.
|
||||
* If set `cutOnly`, will just remove the over decimal part.
|
||||
*/
|
||||
export declare function toFixed(numStr: string, separatorStr: string, precision?: number, cutOnly?: boolean): string;
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "BigIntDecimal", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _BigIntDecimal.default;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "NumberDecimal", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _NumberDecimal.default;
|
||||
}
|
||||
});
|
||||
exports.default = getMiniDecimal;
|
||||
exports.toFixed = toFixed;
|
||||
var _BigIntDecimal = _interopRequireDefault(require("./BigIntDecimal"));
|
||||
var _NumberDecimal = _interopRequireDefault(require("./NumberDecimal"));
|
||||
var _numberUtil = require("./numberUtil");
|
||||
var _supportUtil = require("./supportUtil");
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
// Still support origin export
|
||||
|
||||
function getMiniDecimal(value) {
|
||||
// We use BigInt here.
|
||||
// Will fallback to Number if not support.
|
||||
if ((0, _supportUtil.supportBigInt)()) {
|
||||
return new _BigIntDecimal.default(value);
|
||||
}
|
||||
return new _NumberDecimal.default(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the logic of toFixed to around like 1.5 => 2.
|
||||
* If set `cutOnly`, will just remove the over decimal part.
|
||||
*/
|
||||
function toFixed(numStr, separatorStr, precision) {
|
||||
var cutOnly = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
||||
if (numStr === '') {
|
||||
return '';
|
||||
}
|
||||
var _trimNumber = (0, _numberUtil.trimNumber)(numStr),
|
||||
negativeStr = _trimNumber.negativeStr,
|
||||
integerStr = _trimNumber.integerStr,
|
||||
decimalStr = _trimNumber.decimalStr;
|
||||
var precisionDecimalStr = "".concat(separatorStr).concat(decimalStr);
|
||||
var numberWithoutDecimal = "".concat(negativeStr).concat(integerStr);
|
||||
if (precision >= 0) {
|
||||
// We will get last + 1 number to check if need advanced number
|
||||
var advancedNum = Number(decimalStr[precision]);
|
||||
if (advancedNum >= 5 && !cutOnly) {
|
||||
var advancedDecimal = getMiniDecimal(numStr).add("".concat(negativeStr, "0.").concat('0'.repeat(precision)).concat(10 - advancedNum));
|
||||
return toFixed(advancedDecimal.toString(), separatorStr, precision, cutOnly);
|
||||
}
|
||||
if (precision === 0) {
|
||||
return numberWithoutDecimal;
|
||||
}
|
||||
return "".concat(numberWithoutDecimal).concat(separatorStr).concat(decimalStr.padEnd(precision, '0').slice(0, precision));
|
||||
}
|
||||
if (precisionDecimalStr === '.0') {
|
||||
return numberWithoutDecimal;
|
||||
}
|
||||
return "".concat(numberWithoutDecimal).concat(precisionDecimalStr);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import type { DecimalClass, ValueType } from './interface';
|
||||
/**
|
||||
* We can remove this when IE not support anymore
|
||||
*/
|
||||
export default class NumberDecimal implements DecimalClass {
|
||||
origin: string;
|
||||
number: number;
|
||||
empty: boolean;
|
||||
constructor(value: ValueType);
|
||||
negate(): NumberDecimal;
|
||||
add(value: ValueType): NumberDecimal;
|
||||
multi(value: ValueType): NumberDecimal;
|
||||
isEmpty(): boolean;
|
||||
isNaN(): boolean;
|
||||
isInvalidate(): boolean;
|
||||
equals(target: DecimalClass): boolean;
|
||||
lessEquals(target: DecimalClass): boolean;
|
||||
toNumber(): number;
|
||||
toString(safe?: boolean): string;
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
||||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
||||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
||||
var _numberUtil = require("./numberUtil");
|
||||
/**
|
||||
* We can remove this when IE not support anymore
|
||||
*/
|
||||
var NumberDecimal = exports.default = /*#__PURE__*/function () {
|
||||
function NumberDecimal(value) {
|
||||
(0, _classCallCheck2.default)(this, NumberDecimal);
|
||||
(0, _defineProperty2.default)(this, "origin", '');
|
||||
(0, _defineProperty2.default)(this, "number", void 0);
|
||||
(0, _defineProperty2.default)(this, "empty", void 0);
|
||||
if ((0, _numberUtil.isEmpty)(value)) {
|
||||
this.empty = true;
|
||||
return;
|
||||
}
|
||||
this.origin = String(value);
|
||||
this.number = Number(value);
|
||||
}
|
||||
(0, _createClass2.default)(NumberDecimal, [{
|
||||
key: "negate",
|
||||
value: function negate() {
|
||||
return new NumberDecimal(-this.toNumber());
|
||||
}
|
||||
}, {
|
||||
key: "add",
|
||||
value: function add(value) {
|
||||
if (this.isInvalidate()) {
|
||||
return new NumberDecimal(value);
|
||||
}
|
||||
var target = Number(value);
|
||||
if (Number.isNaN(target)) {
|
||||
return this;
|
||||
}
|
||||
var number = this.number + target;
|
||||
|
||||
// [Legacy] Back to safe integer
|
||||
if (number > Number.MAX_SAFE_INTEGER) {
|
||||
return new NumberDecimal(Number.MAX_SAFE_INTEGER);
|
||||
}
|
||||
if (number < Number.MIN_SAFE_INTEGER) {
|
||||
return new NumberDecimal(Number.MIN_SAFE_INTEGER);
|
||||
}
|
||||
var maxPrecision = Math.max((0, _numberUtil.getNumberPrecision)(this.number), (0, _numberUtil.getNumberPrecision)(target));
|
||||
return new NumberDecimal(number.toFixed(maxPrecision));
|
||||
}
|
||||
}, {
|
||||
key: "multi",
|
||||
value: function multi(value) {
|
||||
var target = Number(value);
|
||||
if (this.isInvalidate() || Number.isNaN(target)) {
|
||||
return new NumberDecimal(NaN);
|
||||
}
|
||||
var number = this.number * target;
|
||||
|
||||
// [Legacy] Back to safe integer
|
||||
if (number > Number.MAX_SAFE_INTEGER) {
|
||||
return new NumberDecimal(Number.MAX_SAFE_INTEGER);
|
||||
}
|
||||
if (number < Number.MIN_SAFE_INTEGER) {
|
||||
return new NumberDecimal(Number.MIN_SAFE_INTEGER);
|
||||
}
|
||||
var maxPrecision = Math.max((0, _numberUtil.getNumberPrecision)(this.number), (0, _numberUtil.getNumberPrecision)(target));
|
||||
return new NumberDecimal(number.toFixed(maxPrecision));
|
||||
}
|
||||
}, {
|
||||
key: "isEmpty",
|
||||
value: function isEmpty() {
|
||||
return this.empty;
|
||||
}
|
||||
}, {
|
||||
key: "isNaN",
|
||||
value: function isNaN() {
|
||||
return Number.isNaN(this.number);
|
||||
}
|
||||
}, {
|
||||
key: "isInvalidate",
|
||||
value: function isInvalidate() {
|
||||
return this.isEmpty() || this.isNaN();
|
||||
}
|
||||
}, {
|
||||
key: "equals",
|
||||
value: function equals(target) {
|
||||
return this.toNumber() === (target === null || target === void 0 ? void 0 : target.toNumber());
|
||||
}
|
||||
}, {
|
||||
key: "lessEquals",
|
||||
value: function lessEquals(target) {
|
||||
return this.add(target.negate().toString()).toNumber() <= 0;
|
||||
}
|
||||
}, {
|
||||
key: "toNumber",
|
||||
value: function toNumber() {
|
||||
return this.number;
|
||||
}
|
||||
}, {
|
||||
key: "toString",
|
||||
value: function toString() {
|
||||
var safe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
||||
if (!safe) {
|
||||
return this.origin;
|
||||
}
|
||||
if (this.isInvalidate()) {
|
||||
return '';
|
||||
}
|
||||
if ((0, _numberUtil.isE)(this.number) && (0, _numberUtil.getNumberPrecision)(this.number) > 100) {
|
||||
return String(this.number);
|
||||
}
|
||||
return (0, _numberUtil.num2str)(this.number);
|
||||
}
|
||||
}]);
|
||||
return NumberDecimal;
|
||||
}();
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import getMiniDecimal from './MiniDecimal';
|
||||
export * from './MiniDecimal';
|
||||
import { trimNumber, getNumberPrecision, num2str, validateNumber } from './numberUtil';
|
||||
export { trimNumber, getNumberPrecision, num2str, validateNumber };
|
||||
export default getMiniDecimal;
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
var _exportNames = {
|
||||
trimNumber: true,
|
||||
getNumberPrecision: true,
|
||||
num2str: true,
|
||||
validateNumber: true
|
||||
};
|
||||
exports.default = void 0;
|
||||
Object.defineProperty(exports, "getNumberPrecision", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _numberUtil.getNumberPrecision;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "num2str", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _numberUtil.num2str;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "trimNumber", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _numberUtil.trimNumber;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "validateNumber", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _numberUtil.validateNumber;
|
||||
}
|
||||
});
|
||||
var _MiniDecimal = _interopRequireWildcard(require("./MiniDecimal"));
|
||||
Object.keys(_MiniDecimal).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
||||
if (key in exports && exports[key] === _MiniDecimal[key]) return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _MiniDecimal[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
var _numberUtil = require("./numberUtil");
|
||||
var _default = exports.default = _MiniDecimal.default;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
export type ValueType = string | number;
|
||||
export interface DecimalClass {
|
||||
add: (value: ValueType) => DecimalClass;
|
||||
multi: (value: ValueType) => DecimalClass;
|
||||
isEmpty: () => boolean;
|
||||
isNaN: () => boolean;
|
||||
isInvalidate: () => boolean;
|
||||
toNumber: () => number;
|
||||
/**
|
||||
* Parse value as string. Will return empty string if `isInvalidate`.
|
||||
* You can set `safe=false` to get origin string content.
|
||||
*/
|
||||
toString: (safe?: boolean) => string;
|
||||
equals: (target: DecimalClass) => boolean;
|
||||
lessEquals: (target: DecimalClass) => boolean;
|
||||
negate: () => DecimalClass;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import type { ValueType } from './interface';
|
||||
export declare function isEmpty(value: ValueType): boolean;
|
||||
/**
|
||||
* Format string number to readable number
|
||||
*/
|
||||
export declare function trimNumber(numStr: string): {
|
||||
negative: boolean;
|
||||
negativeStr: string;
|
||||
trimStr: string;
|
||||
integerStr: string;
|
||||
decimalStr: string;
|
||||
fullStr: string;
|
||||
};
|
||||
export declare function isE(number: string | number): boolean;
|
||||
/**
|
||||
* [Legacy] Convert 1e-9 to 0.000000001.
|
||||
* This may lose some precision if user really want 1e-9.
|
||||
*/
|
||||
export declare function getNumberPrecision(number: string | number): number;
|
||||
/**
|
||||
* Convert number (includes scientific notation) to -xxx.yyy format
|
||||
*/
|
||||
export declare function num2str(number: number): string;
|
||||
export declare function validateNumber(num: string | number): boolean;
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.getNumberPrecision = getNumberPrecision;
|
||||
exports.isE = isE;
|
||||
exports.isEmpty = isEmpty;
|
||||
exports.num2str = num2str;
|
||||
exports.trimNumber = trimNumber;
|
||||
exports.validateNumber = validateNumber;
|
||||
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
||||
var _supportUtil = require("./supportUtil");
|
||||
function isEmpty(value) {
|
||||
return !value && value !== 0 && !Number.isNaN(value) || !String(value).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format string number to readable number
|
||||
*/
|
||||
function trimNumber(numStr) {
|
||||
var str = numStr.trim();
|
||||
var negative = str.startsWith('-');
|
||||
if (negative) {
|
||||
str = str.slice(1);
|
||||
}
|
||||
str = str
|
||||
// Remove decimal 0. `1.000` => `1.`, `1.100` => `1.1`
|
||||
.replace(/(\.\d*[^0])0*$/, '$1')
|
||||
// Remove useless decimal. `1.` => `1`
|
||||
.replace(/\.0*$/, '')
|
||||
// Remove integer 0. `0001` => `1`, 000.1' => `.1`
|
||||
.replace(/^0+/, '');
|
||||
if (str.startsWith('.')) {
|
||||
str = "0".concat(str);
|
||||
}
|
||||
var trimStr = str || '0';
|
||||
var splitNumber = trimStr.split('.');
|
||||
var integerStr = splitNumber[0] || '0';
|
||||
var decimalStr = splitNumber[1] || '0';
|
||||
if (integerStr === '0' && decimalStr === '0') {
|
||||
negative = false;
|
||||
}
|
||||
var negativeStr = negative ? '-' : '';
|
||||
return {
|
||||
negative: negative,
|
||||
negativeStr: negativeStr,
|
||||
trimStr: trimStr,
|
||||
integerStr: integerStr,
|
||||
decimalStr: decimalStr,
|
||||
fullStr: "".concat(negativeStr).concat(trimStr)
|
||||
};
|
||||
}
|
||||
function isE(number) {
|
||||
var str = String(number);
|
||||
return !Number.isNaN(Number(str)) && str.includes('e');
|
||||
}
|
||||
/**
|
||||
* Parse a scientific-notation string into reusable parts.
|
||||
*
|
||||
* The idea is to split the value into mantissa and exponent first, then
|
||||
* normalize the mantissa into sign, integer/decimal segments, and a compact
|
||||
* digit sequence so later logic can move the decimal point without re-parsing.
|
||||
*/
|
||||
function parseScientificNotation(numStr) {
|
||||
var _numStr$toLowerCase$s = numStr.toLowerCase().split('e'),
|
||||
_numStr$toLowerCase$s2 = (0, _slicedToArray2.default)(_numStr$toLowerCase$s, 2),
|
||||
mantissa = _numStr$toLowerCase$s2[0],
|
||||
_numStr$toLowerCase$s3 = _numStr$toLowerCase$s2[1],
|
||||
exponent = _numStr$toLowerCase$s3 === void 0 ? '0' : _numStr$toLowerCase$s3;
|
||||
var negative = mantissa.startsWith('-');
|
||||
var unsignedMantissa = negative ? mantissa.slice(1) : mantissa;
|
||||
var _unsignedMantissa$spl = unsignedMantissa.split('.'),
|
||||
_unsignedMantissa$spl2 = (0, _slicedToArray2.default)(_unsignedMantissa$spl, 2),
|
||||
_unsignedMantissa$spl3 = _unsignedMantissa$spl2[0],
|
||||
integer = _unsignedMantissa$spl3 === void 0 ? '0' : _unsignedMantissa$spl3,
|
||||
_unsignedMantissa$spl4 = _unsignedMantissa$spl2[1],
|
||||
decimal = _unsignedMantissa$spl4 === void 0 ? '' : _unsignedMantissa$spl4;
|
||||
var digits = "".concat(integer).concat(decimal).replace(/^0+/, '') || '0';
|
||||
return {
|
||||
decimal: decimal,
|
||||
digits: digits,
|
||||
exponent: Number(exponent),
|
||||
integer: integer,
|
||||
negative: negative
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand parsed scientific notation into a plain decimal string.
|
||||
*
|
||||
* The core idea is to calculate where the decimal point lands after applying
|
||||
* the exponent, then rebuild the string by either padding zeros or inserting
|
||||
* the decimal point inside the normalized digit sequence.
|
||||
*/
|
||||
function expandScientificNotation(parsed) {
|
||||
var decimal = parsed.decimal,
|
||||
digits = parsed.digits,
|
||||
exponent = parsed.exponent,
|
||||
integer = parsed.integer,
|
||||
negative = parsed.negative;
|
||||
if (digits === '0') {
|
||||
return '0';
|
||||
}
|
||||
var integerDigits = integer.replace(/^0+/, '').length;
|
||||
var leadingDecimalZeros = (decimal.match(/^0*/) || [''])[0].length;
|
||||
var initialDecimalIndex = integerDigits || -leadingDecimalZeros;
|
||||
var decimalIndex = initialDecimalIndex + exponent;
|
||||
var expanded = '';
|
||||
if (decimalIndex <= 0) {
|
||||
expanded = "0.".concat('0'.repeat(-decimalIndex)).concat(digits);
|
||||
} else if (decimalIndex >= digits.length) {
|
||||
expanded = "".concat(digits).concat('0'.repeat(decimalIndex - digits.length));
|
||||
} else {
|
||||
expanded = "".concat(digits.slice(0, decimalIndex), ".").concat(digits.slice(decimalIndex));
|
||||
}
|
||||
return "".concat(negative ? '-' : '').concat(expanded);
|
||||
}
|
||||
function getScientificPrecision(parsed) {
|
||||
if (parsed.exponent >= 0) {
|
||||
return Math.max(0, parsed.decimal.length - parsed.exponent);
|
||||
}
|
||||
return Math.abs(parsed.exponent) + parsed.decimal.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Legacy] Convert 1e-9 to 0.000000001.
|
||||
* This may lose some precision if user really want 1e-9.
|
||||
*/
|
||||
function getNumberPrecision(number) {
|
||||
var numStr = String(number);
|
||||
if (isE(number)) {
|
||||
return getScientificPrecision(parseScientificNotation(numStr));
|
||||
}
|
||||
return numStr.includes('.') && validateNumber(numStr) ? numStr.length - numStr.indexOf('.') - 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert number (includes scientific notation) to -xxx.yyy format
|
||||
*/
|
||||
function num2str(number) {
|
||||
var numStr = String(number);
|
||||
if (isE(number)) {
|
||||
if (number > Number.MAX_SAFE_INTEGER) {
|
||||
return String((0, _supportUtil.supportBigInt)() ? BigInt(number).toString() : Number.MAX_SAFE_INTEGER);
|
||||
}
|
||||
if (number < Number.MIN_SAFE_INTEGER) {
|
||||
return String((0, _supportUtil.supportBigInt)() ? BigInt(number).toString() : Number.MIN_SAFE_INTEGER);
|
||||
}
|
||||
var parsed = parseScientificNotation(numStr);
|
||||
var precision = getScientificPrecision(parsed);
|
||||
numStr = precision > 100 ? expandScientificNotation(parsed) : number.toFixed(precision);
|
||||
}
|
||||
return trimNumber(numStr).fullStr;
|
||||
}
|
||||
function validateNumber(num) {
|
||||
if (typeof num === 'number') {
|
||||
return !Number.isNaN(num);
|
||||
}
|
||||
|
||||
// Empty
|
||||
if (!num) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
// Normal type: 11.28
|
||||
/^\s*-?\d+(\.\d+)?\s*$/.test(num) ||
|
||||
// Pre-number: 1.
|
||||
/^\s*-?\d+\.\s*$/.test(num) ||
|
||||
// Post-number: .1
|
||||
/^\s*-?\.\d+\s*$/.test(num)
|
||||
);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare function supportBigInt(): boolean;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.supportBigInt = supportBigInt;
|
||||
function supportBigInt() {
|
||||
return typeof BigInt === 'function';
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"name": "@rc-component/mini-decimal",
|
||||
"version": "1.1.3",
|
||||
"description": "Lite lib to only support decimal add calculation",
|
||||
"keywords": [
|
||||
"decimal"
|
||||
],
|
||||
"homepage": "https://github.com/react-component/mini-decimal",
|
||||
"bugs": {
|
||||
"url": "https://github.com/react-component/mini-decimal/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/react-component/mini-decimal.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "smith3816@gmail.com",
|
||||
"main": "./lib/index",
|
||||
"module": "./es/index",
|
||||
"typings": "es/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"es"
|
||||
],
|
||||
"scripts": {
|
||||
"compile": "father build",
|
||||
"deploy": "npm run docs:build && npm run docs:deploy",
|
||||
"docs:build": "dumi build",
|
||||
"docs:deploy": "gh-pages -d dist",
|
||||
"coverage": "rc-test --coverage",
|
||||
"lint": "eslint src/ --ext .tsx,.ts",
|
||||
"lint:tsc": "tsc -p tsconfig.json --noEmit",
|
||||
"now-build": "npm run docs:build",
|
||||
"prepublishOnly": "npm run compile && rc-np",
|
||||
"prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"",
|
||||
"start": "dumi dev",
|
||||
"test": "rc-test",
|
||||
"watch": "father dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rc-component/father-plugin": "^2.0.4",
|
||||
"@rc-component/np": "^1.0.4",
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^22.1.0",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@umijs/fabric": "^4.0.1",
|
||||
"dumi": "^2.3.8",
|
||||
"eslint": "^8.57.0",
|
||||
"father": "^4.4.4",
|
||||
"gh-pages": "^6.1.1",
|
||||
"prettier": "^3.3.3",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"rc-test": "^7.0.15",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.x"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user