bigint-decimal 1.0.20 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +282 -84
- package/dist/common/bigintDecimalBase.d.ts +31 -0
- package/dist/common/bigintDecimalBase.d.ts.map +1 -0
- package/dist/common/bigintDecimalBase.js +268 -0
- package/dist/common/bigintDecimalBase.js.map +1 -0
- package/dist/common/index.d.ts +144 -26
- package/dist/common/index.d.ts.map +1 -1
- package/dist/common/index.js +771 -222
- package/dist/common/index.js.map +1 -1
- package/dist/global/bigintDecimalBase.d.ts +31 -0
- package/dist/global/bigintDecimalBase.d.ts.map +1 -0
- package/dist/global/bigintDecimalBase.js +264 -0
- package/dist/global/bigintDecimalBase.js.map +1 -0
- package/dist/global/index.d.ts +144 -26
- package/dist/global/index.d.ts.map +1 -1
- package/dist/global/index.js +771 -222
- package/dist/global/index.js.map +1 -1
- package/package.json +9 -3
- package/tsconfig.base.json +2 -1
package/README.md
CHANGED
|
@@ -1,27 +1,52 @@
|
|
|
1
|
-
#
|
|
1
|
+
# bigint-decimal
|
|
2
2
|
|
|
3
|
-
[![
|
|
3
|
+
[![License: MIT][license-src]][license-href]
|
|
4
4
|
[![npm version][npm-version-src]][npm-version-href]
|
|
5
5
|
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
[
|
|
9
|
-
[standard-js-href]: https://github.com/Generalsimus/bigint-decimal/blob/main/LICENSE
|
|
10
|
-
|
|
7
|
+
[license-src]: https://img.shields.io/badge/license-MIT-brightgreen?&style=flat-square
|
|
8
|
+
[license-href]: https://github.com/Generalsimus/bigint-decimal/blob/main/LICENSE
|
|
11
9
|
[npm-version-src]: https://img.shields.io/npm/v/bigint-decimal?&style=flat-square
|
|
12
10
|
[npm-version-href]: https://www.npmjs.com/package/bigint-decimal
|
|
13
|
-
|
|
14
11
|
[npm-downloads-src]: https://img.shields.io/npm/dt/bigint-decimal?&style=flat-square
|
|
15
12
|
[npm-downloads-href]: https://www.npmjs.com/package/bigint-decimal
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
Exact decimal arithmetic and `Math` functions at any precision, built on native `BigInt`.
|
|
15
|
+
|
|
16
|
+
JavaScript numbers are binary floating point, so everyday decimals come out wrong:
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
0.1 + 0.2; // 0.30000000000000004
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`bigint-decimal` stores every number exactly, as a `BigInt` plus a count of decimal places:
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import { BigintDecimal } from "bigint-decimal";
|
|
26
|
+
|
|
27
|
+
new BigintDecimal("0.1").plus("0.2").toString(); // "0.3"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- **Exact** `plus`, `minus` and `times`, with no rounding errors.
|
|
31
|
+
- **The `Math` functions**: `sqrt`, `pow`, `exp`, `log`, `sin`, `atan2` and more, plus `PI`, `E` and the other constants.
|
|
32
|
+
- **Any precision**: 20 decimal places by default, or thousands if you need them.
|
|
33
|
+
- **Correctly rounded**: results are checked digit by digit against independent references.
|
|
34
|
+
- **Fast**: faster than decimal.js, bignumber.js and big.js in every [benchmark](#benchmarks).
|
|
35
|
+
- **Zero dependencies**, TypeScript types included, ESM and CommonJS.
|
|
19
36
|
|
|
20
|
-
|
|
37
|
+
## Table of Contents
|
|
21
38
|
|
|
22
|
-
|
|
39
|
+
- [Install](#install)
|
|
40
|
+
- [Usage](#usage)
|
|
41
|
+
- [API](#api)
|
|
42
|
+
- [How it works](#how-it-works)
|
|
43
|
+
- [Benchmarks](#benchmarks)
|
|
44
|
+
- [Testing](#testing)
|
|
45
|
+
- [Limitations](#limitations)
|
|
46
|
+
- [Contributing](#contributing)
|
|
47
|
+
- [License](#license)
|
|
23
48
|
|
|
24
|
-
##
|
|
49
|
+
## Install
|
|
25
50
|
|
|
26
51
|
```bash
|
|
27
52
|
npm install bigint-decimal
|
|
@@ -29,132 +54,305 @@ npm install bigint-decimal
|
|
|
29
54
|
yarn add bigint-decimal
|
|
30
55
|
# or
|
|
31
56
|
pnpm add bigint-decimal
|
|
57
|
+
```
|
|
32
58
|
|
|
59
|
+
Works in Node.js 14+ and all modern browsers, with both `import` and `require`:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
import { BigintDecimal } from "bigint-decimal"; // ES modules
|
|
63
|
+
const { BigintDecimal } = require("bigint-decimal"); // CommonJS
|
|
33
64
|
```
|
|
34
65
|
|
|
35
|
-
##
|
|
66
|
+
## Usage
|
|
36
67
|
|
|
37
|
-
|
|
68
|
+
### Quick start
|
|
38
69
|
|
|
39
70
|
```javascript
|
|
40
|
-
|
|
71
|
+
import { BigintDecimal } from "bigint-decimal";
|
|
72
|
+
|
|
73
|
+
const price = new BigintDecimal("19.99");
|
|
74
|
+
const total = price.times(3).times("1.0825").round(2); // 3 items plus 8.25% tax, rounded to cents
|
|
41
75
|
|
|
76
|
+
total.toString(); // "64.92"
|
|
77
|
+
total.eq("64.92"); // true
|
|
42
78
|
```
|
|
43
79
|
|
|
44
|
-
`
|
|
80
|
+
Methods return `BigintDecimal` objects, not strings. In the examples below, a comment shows the value as `toString()` returns it. Compare values with `eq`, `gt` and the other [comparison methods](#comparing), not with `===`.
|
|
81
|
+
|
|
82
|
+
### Creating numbers
|
|
83
|
+
|
|
84
|
+
Pass strings, numbers, bigints or another `BigintDecimal`. Scientific notation works too.
|
|
45
85
|
|
|
46
86
|
```javascript
|
|
47
|
-
|
|
87
|
+
new BigintDecimal("123.45"); // "123.45"
|
|
88
|
+
new BigintDecimal("1e-18"); // "0.000000000000000001"
|
|
89
|
+
new BigintDecimal("1.5e3"); // "1500"
|
|
90
|
+
new BigintDecimal(100n); // "100"
|
|
91
|
+
new BigintDecimal(); // "0"
|
|
92
|
+
```
|
|
48
93
|
|
|
49
|
-
|
|
50
|
-
const b = new BigintDecimal("0.2");
|
|
51
|
-
console.log(a.plus(b)); // "0.3" ✅
|
|
94
|
+
Prefer strings for decimals. A JavaScript `number` is already rounded before the library sees it:
|
|
52
95
|
|
|
96
|
+
```javascript
|
|
97
|
+
new BigintDecimal(0.1 + 0.2); // "0.30000000000000004"
|
|
98
|
+
new BigintDecimal("0.3"); // "0.3"
|
|
53
99
|
```
|
|
54
100
|
|
|
55
|
-
|
|
101
|
+
Anything that is not a number throws an error instead of silently becoming `0`:
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
new BigintDecimal("abc"); // Error: "abc" is not safe parameter .
|
|
105
|
+
new BigintDecimal("-"); // Error: "-" is not safe parameter .
|
|
106
|
+
```
|
|
56
107
|
|
|
57
|
-
###
|
|
108
|
+
### Arithmetic
|
|
58
109
|
|
|
59
|
-
|
|
110
|
+
Every operation returns a new `BigintDecimal`, so calls chain. The original value never changes.
|
|
60
111
|
|
|
61
|
-
```
|
|
62
|
-
|
|
112
|
+
```javascript
|
|
113
|
+
new BigintDecimal("1500.50").plus("200.75"); // "1701.25"
|
|
114
|
+
new BigintDecimal("1701.25").minus("0.005"); // "1701.245"
|
|
115
|
+
new BigintDecimal("45000.50").times("0.5"); // "22500.25"
|
|
116
|
+
new BigintDecimal("1000").div("3"); // "333.33333333333333333333"
|
|
63
117
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const gasFee = new BigintDecimal("0.005");
|
|
118
|
+
new BigintDecimal("100").plus("50.5").times("2").minus("1.1").div("2"); // "149.95"
|
|
119
|
+
```
|
|
67
120
|
|
|
68
|
-
|
|
69
|
-
|
|
121
|
+
- `plus`, `minus` and `times` are always exact.
|
|
122
|
+
- `div` stops at the [precision](#precision), 20 decimal places by default, and cuts off the remaining digits: `2 / 3` gives `0.66666666666666666666`.
|
|
123
|
+
- Dividing by zero throws `Error: Division by zero`.
|
|
70
124
|
|
|
71
|
-
|
|
72
|
-
const finalBalance = newBalance.minus(gasFee); // "1701.245"
|
|
125
|
+
### Comparing
|
|
73
126
|
|
|
74
|
-
|
|
75
|
-
const price = new BigintDecimal("45000.50");
|
|
76
|
-
const amount = new BigintDecimal("0.5");
|
|
77
|
-
const totalValue = price.times(amount); // "22500.25"
|
|
127
|
+
Comparisons return `true` or `false` and compare exact values, so `"1.50"` equals `"1.5"`.
|
|
78
128
|
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const splits = new BigintDecimal("3");
|
|
82
|
-
const perPerson = totalTokens.div(splits); // "333.33333333333333333333"
|
|
129
|
+
```javascript
|
|
130
|
+
const balance = new BigintDecimal("999.99");
|
|
83
131
|
|
|
132
|
+
balance.gt("1000"); // false (greater than)
|
|
133
|
+
balance.gte("1000"); // false (greater than or equal)
|
|
134
|
+
balance.lt("1000"); // true (less than)
|
|
135
|
+
balance.lte("1000"); // true (less than or equal)
|
|
136
|
+
new BigintDecimal("1.50").eq("1.5"); // true
|
|
84
137
|
```
|
|
85
138
|
|
|
86
|
-
###
|
|
139
|
+
### Rounding
|
|
140
|
+
|
|
141
|
+
`round(decimals)` rounds to a number of decimal places. Halves round away from zero.
|
|
87
142
|
|
|
88
|
-
|
|
143
|
+
```javascript
|
|
144
|
+
new BigintDecimal("1.555").round(2); // "1.56"
|
|
145
|
+
new BigintDecimal("-1.555").round(2); // "-1.56"
|
|
146
|
+
new BigintDecimal("1.555").round(0); // "2"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`BigintDecimal.floor`, `ceil`, `trunc` and `round` round to whole numbers, like `Math`. See [Math functions](#math-functions).
|
|
89
150
|
|
|
90
|
-
|
|
91
|
-
const result = new BigintDecimal("100")
|
|
92
|
-
.plus("50.5")
|
|
93
|
-
.times("2")
|
|
94
|
-
.minus("1.1")
|
|
95
|
-
.div("2");
|
|
151
|
+
### Converting to strings
|
|
96
152
|
|
|
97
|
-
console.log
|
|
153
|
+
`toString()` returns the exact value without trailing zeros. `JSON.stringify` and `console.log` use the same string.
|
|
98
154
|
|
|
155
|
+
```javascript
|
|
156
|
+
new BigintDecimal("12.500").toString(); // "12.5"
|
|
157
|
+
JSON.stringify({ total: new BigintDecimal("12.50") }); // '{"total":"12.5"}'
|
|
99
158
|
```
|
|
100
159
|
|
|
101
|
-
###
|
|
160
|
+
### Precision
|
|
161
|
+
|
|
162
|
+
Precision is the number of decimal places that `div` and the math functions compute. The default is 20.
|
|
102
163
|
|
|
103
|
-
|
|
164
|
+
```javascript
|
|
165
|
+
BigintDecimal.setPrecisionGlobal(2);
|
|
166
|
+
new BigintDecimal("1").div("3"); // "0.33"
|
|
167
|
+
BigintDecimal.sqrt(2); // "1.41"
|
|
104
168
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
169
|
+
BigintDecimal.setPrecisionGlobal(50);
|
|
170
|
+
BigintDecimal.PI; // "3.14159265358979323846264338327950288419716939937511"
|
|
171
|
+
```
|
|
108
172
|
|
|
109
|
-
|
|
110
|
-
userBalance.gte(requiredMargin); // false (Greater Than or Equal)
|
|
111
|
-
userBalance.lt(requiredMargin); // true (Less Than)
|
|
112
|
-
userBalance.lte(requiredMargin); // true (Less Than or Equal)
|
|
113
|
-
userBalance.eq("999.99"); // true (Equal)
|
|
173
|
+
`setPrecision` changes the precision of one value's own `div`. Results of later operations are new values, which use the global precision again.
|
|
114
174
|
|
|
175
|
+
```javascript
|
|
176
|
+
const x = new BigintDecimal("1");
|
|
177
|
+
x.setPrecision(5);
|
|
178
|
+
x.div("3"); // "0.33333"
|
|
179
|
+
x.plus("1").div("3"); // "0.66666666666666666666" (new value, global precision)
|
|
115
180
|
```
|
|
116
181
|
|
|
117
|
-
|
|
182
|
+
Precision must be a whole number `>= 0`. Anything else throws a `RangeError`.
|
|
118
183
|
|
|
119
|
-
|
|
184
|
+
### Math functions
|
|
120
185
|
|
|
121
|
-
|
|
122
|
-
const fee = new BigintDecimal("1.555");
|
|
123
|
-
console.log(fee.round(2)); // "1.56"
|
|
124
|
-
console.log(fee.round(0)); // "2"
|
|
186
|
+
Every function of the JavaScript `Math` object is a static method of `BigintDecimal`, except `fround`, `f16round`, `clz32` and `imul`, which only make sense for floating-point and 32-bit integers. They take the same values as the constructor and return a `BigintDecimal`, rounded to the global precision.
|
|
125
187
|
|
|
188
|
+
```javascript
|
|
189
|
+
BigintDecimal.sqrt("2"); // "1.4142135623730950488"
|
|
190
|
+
BigintDecimal.cbrt("-27"); // "-3"
|
|
191
|
+
BigintDecimal.pow("1.05", 10); // "1.62889462677744140625"
|
|
192
|
+
BigintDecimal.pow(2, 100); // "1267650600228229401496703205376"
|
|
193
|
+
BigintDecimal.exp(1); // "2.71828182845904523536"
|
|
194
|
+
BigintDecimal.log(10); // "2.30258509299404568402"
|
|
195
|
+
BigintDecimal.log10(1000); // "3"
|
|
196
|
+
BigintDecimal.atan2(1, 1); // "0.78539816339744830962"
|
|
197
|
+
BigintDecimal.hypot(3, 4); // "5"
|
|
198
|
+
BigintDecimal.max("1.5", 2n, -3); // "2"
|
|
199
|
+
|
|
200
|
+
BigintDecimal.PI; // "3.14159265358979323846"
|
|
201
|
+
BigintDecimal.E; // "2.71828182845904523536"
|
|
126
202
|
```
|
|
127
203
|
|
|
128
|
-
|
|
204
|
+
They work detached from the class, just like `Math.sqrt`:
|
|
129
205
|
|
|
130
|
-
|
|
206
|
+
```javascript
|
|
207
|
+
["4", "2.25"].map(BigintDecimal.sqrt).map(String); // ["2", "1.5"]
|
|
208
|
+
```
|
|
131
209
|
|
|
132
|
-
|
|
133
|
-
// Change instance precision
|
|
134
|
-
const exactDiv = new BigintDecimal("1");
|
|
135
|
-
exactDiv.setPrecision(5);
|
|
136
|
-
console.log(exactDiv.div("3")); // "0.33333"
|
|
210
|
+
`sumPrecise` adds a whole list exactly, so small values are never lost next to big ones:
|
|
137
211
|
|
|
138
|
-
|
|
139
|
-
BigintDecimal.
|
|
140
|
-
|
|
212
|
+
```javascript
|
|
213
|
+
BigintDecimal.sumPrecise(["1e30", "1e-30", "-1e30"]); // "0.000000000000000000000000000001"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Errors
|
|
217
|
+
|
|
218
|
+
Where `Math` would return `NaN` or `Infinity`, `BigintDecimal` throws a `RangeError` with a clear message instead:
|
|
141
219
|
|
|
220
|
+
```javascript
|
|
221
|
+
BigintDecimal.sqrt(-1); // RangeError: sqrt: argument must be >= 0 (NaN)
|
|
222
|
+
BigintDecimal.log(0); // RangeError: log: argument is out of range
|
|
223
|
+
BigintDecimal.asin(2); // RangeError: asin/acos: argument must be between -1 and 1 (NaN)
|
|
224
|
+
BigintDecimal.pow(-8, "0.5"); // RangeError: pow: negative base with a non-integer exponent is NaN
|
|
142
225
|
```
|
|
143
226
|
|
|
144
|
-
##
|
|
227
|
+
## API
|
|
228
|
+
|
|
229
|
+
### Constructor
|
|
230
|
+
|
|
231
|
+
| Signature | Description |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| `new BigintDecimal(value?)` | `value`: `string`, `number`, `bigint` or `BigintDecimal`. Defaults to `0`. Throws an `Error` for anything that is not a number. |
|
|
234
|
+
|
|
235
|
+
### Instance methods
|
|
236
|
+
|
|
237
|
+
Every `value` argument accepts the same values as the constructor. `decimals` is a whole number `>= 0`.
|
|
238
|
+
|
|
239
|
+
| Method | Returns | Description |
|
|
240
|
+
| --- | --- | --- |
|
|
241
|
+
| `plus(value)` | `BigintDecimal` | Exact sum |
|
|
242
|
+
| `minus(value)` | `BigintDecimal` | Exact difference |
|
|
243
|
+
| `times(value)` | `BigintDecimal` | Exact product |
|
|
244
|
+
| `div(value)` | `BigintDecimal` | Quotient to `precision` decimal places, extra digits cut off. Throws on division by zero. |
|
|
245
|
+
| `round(decimals)` | `BigintDecimal` | Rounded to `decimals` places, halves away from zero |
|
|
246
|
+
| `gt(value)`, `gte(value)`, `lt(value)`, `lte(value)`, `eq(value)` | `boolean` | Comparisons |
|
|
247
|
+
| `setPrecision(decimals)` | `void` | Precision of this value's `div` |
|
|
248
|
+
| `toString()`, `toJSON()` | `string` | The exact value, without trailing zeros |
|
|
249
|
+
| `isBigintDecimal(value)` | `boolean` | Whether `value` can be parsed as a number |
|
|
250
|
+
|
|
251
|
+
### Settings
|
|
252
|
+
|
|
253
|
+
| Property or method | Description |
|
|
254
|
+
| --- | --- |
|
|
255
|
+
| `BigintDecimal.precision` | Current global precision (default `20`) |
|
|
256
|
+
| `BigintDecimal.setPrecisionGlobal(decimals)` | Sets the global precision |
|
|
257
|
+
| `BigintDecimal.clearCache()` | Frees the cached constants. Only useful after very high precision work. |
|
|
258
|
+
|
|
259
|
+
### Math functions and constants
|
|
260
|
+
|
|
261
|
+
All are static methods. Results are rounded half away from zero to `BigintDecimal.precision` decimal places.
|
|
262
|
+
|
|
263
|
+
| Group | Functions |
|
|
264
|
+
| --- | --- |
|
|
265
|
+
| Rounding and sign | `abs(x)`, `sign(x)`, `floor(x)`, `ceil(x)`, `round(x)`, `trunc(x)` |
|
|
266
|
+
| Roots and powers | `sqrt(x)`, `cbrt(x)`, `pow(base, exponent)`, `hypot(...values)` |
|
|
267
|
+
| Exponentials and logarithms | `exp(x)`, `expm1(x)`, `log(x)`, `log10(x)`, `log2(x)`, `log1p(x)` |
|
|
268
|
+
| Trigonometry (radians) | `sin(x)`, `cos(x)`, `tan(x)`, `asin(x)`, `acos(x)`, `atan(x)`, `atan2(y, x)` |
|
|
269
|
+
| Hyperbolic | `sinh(x)`, `cosh(x)`, `tanh(x)`, `asinh(x)`, `acosh(x)`, `atanh(x)` |
|
|
270
|
+
| Lists | `max(...values)`, `min(...values)`, `sumPrecise(values)` |
|
|
271
|
+
| Random | `random()`: a random number in `[0, 1)`, using `crypto.getRandomValues` when available |
|
|
272
|
+
| Constants | `E`, `LN2`, `LN10`, `LOG2E`, `LOG10E`, `PI`, `SQRT1_2`, `SQRT2` |
|
|
273
|
+
|
|
274
|
+
Like `Math.round`, `BigintDecimal.round(x)` rounds halves up: `round("2.5")` is `"3"` and `round("-2.5")` is `"-2"`.
|
|
145
275
|
|
|
146
|
-
|
|
276
|
+
## How it works
|
|
147
277
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
new BigintDecimal(100n); // Native BigInt
|
|
278
|
+
- **Numbers** are stored as a `BigInt` of all their digits plus the number of decimal places: `123.45` is `12345n` with 2 decimals. Arithmetic is plain `BigInt` arithmetic, which V8 runs as optimized native code.
|
|
279
|
+
- **Math functions** work internally in binary fixed point with extra guard digits, then round once to the precision. `sqrt` and `cbrt` are computed directly in decimal and are always correctly rounded.
|
|
280
|
+
- **Algorithms** adapt to the precision: Taylor series with argument reduction at normal sizes, then rectangular splitting, binary splitting (the Chudnovsky series for π) and Newton's method as numbers grow to thousands of digits.
|
|
281
|
+
- **Constants** such as π and ln 2 are computed once and cached at the highest precision used so far. `BigintDecimal.clearCache()` frees them.
|
|
153
282
|
|
|
283
|
+
## Benchmarks
|
|
284
|
+
|
|
285
|
+
**In short:** `bigint-decimal` was the fastest in every test, compared with the three most popular decimal libraries.
|
|
286
|
+
|
|
287
|
+
- **Everyday precision (20 digits):** 1.4× faster for basic arithmetic, 6× to 45× faster for math functions.
|
|
288
|
+
- **High precision (1000 digits):** 1.4× faster for basic arithmetic, 30× to 2800× faster for math functions.
|
|
289
|
+
- **Same accuracy:** both `bigint-decimal` and decimal.js give correctly rounded results.
|
|
290
|
+
|
|
291
|
+
### 20 digits
|
|
292
|
+
|
|
293
|
+
Time per call, lower is better. "—" means the library does not have that function.
|
|
294
|
+
|
|
295
|
+
| Test | Speed-up | bigint-decimal | decimal.js | bignumber.js | big.js |
|
|
296
|
+
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
297
|
+
| Basic arithmetic (`plus`, `times`, `div`) | **1.4× faster** | 🏆 **1.11 µs** | 1.58 µs | 3.09 µs | 3.13 µs |
|
|
298
|
+
| Square root (`sqrt`) | **6× faster** | 🏆 **1.02 µs** | 6.37 µs | 6.08 µs | 43 µs |
|
|
299
|
+
| Exponential (`exp`) | **10× faster** | 🏆 **2.88 µs** | 29 µs | — | — |
|
|
300
|
+
| Natural logarithm (`log`) | **14× faster** | 🏆 **1.85 µs** | 25 µs | — | — |
|
|
301
|
+
| Sine (`sin`) | **7× faster** | 🏆 **3.84 µs** | 28 µs | — | — |
|
|
302
|
+
| Arctangent (`atan`) | **45× faster** | 🏆 **3.65 µs** | 163 µs | — | — |
|
|
303
|
+
| Power with a fractional exponent (`pow`) | **10× faster** | 🏆 **6.12 µs** | 62 µs | — | — |
|
|
304
|
+
|
|
305
|
+
### 1000 digits
|
|
306
|
+
|
|
307
|
+
| Test | Speed-up | bigint-decimal | decimal.js | bignumber.js | big.js |
|
|
308
|
+
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
309
|
+
| Basic arithmetic (`plus`, `times`, `div`) | **1.4× faster** | 🏆 **3.85 µs** | 5.32 µs | 15 µs | 68 µs |
|
|
310
|
+
| Square root (`sqrt`) | **30× faster** | 🏆 **64 µs** | 2.6 ms | 1.9 ms | 195 ms |
|
|
311
|
+
| Exponential (`exp`) | **169× faster** | 🏆 **224 µs** | 37.8 ms | — | — |
|
|
312
|
+
| Natural logarithm (`log`) | **586× faster** | 🏆 **215 µs** | 126 ms | — | — |
|
|
313
|
+
| Sine (`sin`) | **73× faster** | 🏆 **416 µs** | 30.5 ms | — | — |
|
|
314
|
+
| Arctangent (`atan`) | **2826× faster** | 🏆 **734 µs** | 2074 ms | — | — |
|
|
315
|
+
| Power with a fractional exponent (`pow`) | **527× faster** | 🏆 **456 µs** | 240 ms | — | — |
|
|
316
|
+
|
|
317
|
+
🏆 marks the fastest library. Speed-up: how much faster `bigint-decimal` is than the fastest other library that has the function.
|
|
318
|
+
|
|
319
|
+
### Accuracy and limits
|
|
320
|
+
|
|
321
|
+
| Test | Result |
|
|
322
|
+
| --- | --- |
|
|
323
|
+
| Accuracy at 1000 digits | **Tie.** For `exp`, `log`, `sin`, `atan`, `sqrt` and `pow`, both `bigint-decimal` and decimal.js give correctly rounded results. |
|
|
324
|
+
| `log(2)` at 2000 digits | **`bigint-decimal` wins.** It works at any precision; decimal.js throws `Precision limit exceeded` above about 1025 digits. |
|
|
325
|
+
|
|
326
|
+
### How it was measured
|
|
327
|
+
|
|
328
|
+
- Libraries: [decimal.js](https://github.com/MikeMcl/decimal.js) 10.6.0, [bignumber.js](https://github.com/MikeMcl/bignumber.js) 11.1.5 and [big.js](https://github.com/MikeMcl/big.js) 7.0.1, all computing the same operation to the same number of digits.
|
|
329
|
+
- Each time is the average over 16 different inputs, measured with [tinybench](https://github.com/tinylibs/tinybench).
|
|
330
|
+
- Node v24.21.0 on an AMD Ryzen 9 7940HS. Run `npm run bench` to reproduce the numbers on your machine.
|
|
331
|
+
|
|
332
|
+
## Testing
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
npm test
|
|
154
336
|
```
|
|
155
337
|
|
|
156
|
-
|
|
338
|
+
The test suite checks results digit by digit, not just roughly:
|
|
339
|
+
|
|
340
|
+
- **Independent references:** about 300 results, from 20 up to 2000 digits, match Python's `decimal` module exactly. They include hard cases next to singularities (`asin` near ±1, `tan` near π/2) and huge arguments such as `sin(1e300)`.
|
|
341
|
+
- **Exact checks:** arithmetic, rounding, `floor`/`ceil`/`round` and correctly rounded `sqrt`/`cbrt` on random big numbers are compared with plain `BigInt` arithmetic.
|
|
342
|
+
- **Memory:** a heavy workload in a separate process shows no memory growth, and `clearCache()` frees the cache.
|
|
343
|
+
- **Coverage:** 100% of statements, branches and functions.
|
|
344
|
+
|
|
345
|
+
## Limitations
|
|
346
|
+
|
|
347
|
+
- **Precision is counted in decimal places**, not significant digits. A result smaller than the precision rounds to `0`: `exp(-50)` is `"0"` at the default 20 places.
|
|
348
|
+
- **`div` cuts off** extra digits instead of rounding them; the math functions round.
|
|
349
|
+
- **One rounding mode** (half away from zero), and one global precision for the math functions.
|
|
350
|
+
- **Untrusted input:** there is no size limit besides memory. A string like `"1e1000000000"` creates a billion-digit number, which can freeze a server. Check the length or exponent of user input before passing it in.
|
|
351
|
+
|
|
352
|
+
## Contributing
|
|
353
|
+
|
|
354
|
+
Questions, bug reports and pull requests are welcome on [GitHub](https://github.com/Generalsimus/bigint-decimal/issues). Please run `npm test` before opening a pull request, and `npm run bench` if your change affects speed.
|
|
157
355
|
|
|
158
356
|
## License
|
|
159
357
|
|
|
160
|
-
MIT
|
|
358
|
+
[MIT](LICENSE) © 2024 soso
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type SafeValues = string | number | bigint;
|
|
2
|
+
export declare class BigintDecimalBase {
|
|
3
|
+
protected value: bigint;
|
|
4
|
+
protected decimals: number;
|
|
5
|
+
static precision: number;
|
|
6
|
+
precision: number;
|
|
7
|
+
constructor(val?: SafeValues | BigintDecimalBase);
|
|
8
|
+
static setPrecisionGlobal(newPrecision: number): void;
|
|
9
|
+
setPrecision(newPrecision: number): void;
|
|
10
|
+
protected static checkDecimals(decimals: number, name: string): number;
|
|
11
|
+
protected create(value: bigint, decimals: number): this;
|
|
12
|
+
protected trim(): this;
|
|
13
|
+
private toSafeValue;
|
|
14
|
+
private getNumberIFoForBigintDecimal;
|
|
15
|
+
toString(): string;
|
|
16
|
+
toJSON(): string;
|
|
17
|
+
isBigintDecimal(val: unknown): boolean;
|
|
18
|
+
plus(val: SafeValues | BigintDecimalBase): this;
|
|
19
|
+
minus(val: SafeValues | BigintDecimalBase): this;
|
|
20
|
+
times(val: SafeValues | BigintDecimalBase): this;
|
|
21
|
+
round(toDecimals: number): this;
|
|
22
|
+
div(val: SafeValues | BigintDecimalBase): this;
|
|
23
|
+
private compare;
|
|
24
|
+
gt(val: SafeValues | BigintDecimalBase): boolean;
|
|
25
|
+
lt(val: SafeValues | BigintDecimalBase): boolean;
|
|
26
|
+
gte(val: SafeValues | BigintDecimalBase): boolean;
|
|
27
|
+
lte(val: SafeValues | BigintDecimalBase): boolean;
|
|
28
|
+
eq(val: SafeValues | BigintDecimalBase): boolean;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=bigintDecimalBase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bigintDecimalBase.d.ts","sourceRoot":"","sources":["../../bigintDecimalBase.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;AAC1C,qBAAa,iBAAiB;IAE1B,SAAS,CAAC,KAAK,EAAE,MAAM,CAAM;IAC7B,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAK;IAC/B,MAAM,CAAC,SAAS,EAAE,MAAM,CAAK;IAC7B,SAAS,EAAE,MAAM,CAA+B;IAEhD,YAAY,GAAG,GAAE,UAAU,GAAG,iBAAuB,EAuBpD;IAED,MAAM,CAAC,kBAAkB,CAAC,YAAY,EAAE,MAAM,QAG7C;IAED,YAAY,CAAC,YAAY,EAAE,MAAM,QAEhC;IAGD,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrE;IAGD,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMtD;IAGD,SAAS,CAAC,IAAI,IAAI,IAAI,CAuBrB;IAED,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,4BAA4B;IAmCpC,QAAQ,IAAI,MAAM,CAoCjB;IAED,MAAM,WAEL;IAMD,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAwBrC;IAED,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,GAAG,IAAI,CAgB9C;IAED,KAAK,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,GAAG,IAAI,CAW/C;IAED,KAAK,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,GAAG,IAAI,CAK/C;IAED,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAa9B;IAED,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,GAAG,IAAI,CAe7C;IAED,OAAO,CAAC,OAAO;IAUf,EAAE,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,WAErC;IAED,EAAE,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,WAErC;IAED,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,WAEtC;IAED,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,WAEtC;IAED,EAAE,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,WAErC;CACJ"}
|