Package Exports
- dnum
- dnum/dist/dnum.es.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (dnum) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
dnum
dnum (Decimal Numbers) is a library that allows to operate on large numbers represented as a pair composed of a BigInt for the value, and a Number for the decimals.
It is not a replacement for libraries such as decimal.js or the native BigInt operators (which it uses internally). Instead, dnum focuses on a small (~1kb) set of utilities focused around the Dnum data structure, allowing to safely operate on numbers represented in various decimal precisions.
The Dnum data structure is a simple array with two entries, or tuple: a BigInt for the value, and a Number for the decimals. This is how it looks in TypeScript:
type Dnum = [value: bigint, decimals: number];Install
npm install --save dnum
pnpm add dnum
yarn add dnumExample
dnum can be useful to manipulate different currencies together, so let’s imagine a situation where you have the price of a given token token TKN expressed in ETH, which you received it as a string to avoid any precision issue:
let tknPriceInEth = "17.30624293209842";And you have the price of ETH in USD, as a number this time:
let ethPriceInUsd = 1002.37;Finally, you have a certain quantity of TKN to be displayed, as a BigInt:
let tknQuantity = 1401385000000000000000n; // 1401.385 with 18 decimals precisionYou want to display the USD value of tknQuantity, which would normally require to:
- Parse the numbers correctly (without using
parseInt()/parseFloat()to avoid precision loss). - Convert everything into BigInt values with an identical decimals precision.
- Multiply the numbers and get the result.
- Convert it into a string to format it − without using
Numbersince you’d lose precision.
dnum can do all of this for you:
// No need to convert anything, you can just multiply different formats of decimal numbers:
let tknPriceInUsd = dnum.multiply(tknPriceInEth, ethPriceInUsd);
// A Dnum is just a two entries array (or tuple): [value: bigint, decimals: number]
let tknQuantityInUsd = dnum.multiply([tknQuantity, 18], tknPriceInUsd);
dnum.format(tknQuantityInUsd, 2); // $24,310,188.17You can play with this example on CodeSandbox.
API
Types
type Dnum = [value: bigint, decimals: number];
type Numberish = string | number | bigint | Dnum;format(value, options)
Formats the number for display purposes.
| Name | Description | Type |
|---|---|---|
value |
The value to format. | Dnum |
options.digits |
Number of digits to display. Setting options to a number acts as an alias for this option. Defaults to the number of decimals in the passed Dnum. |
number |
options.compact |
Compact formatting (e.g. “1,000” becomes “1K”). | object |
options.trailingZeros |
Add trailing zeros if any, following the number of digits. | object |
| returns | Formatted string. | string |
Example
let amount = [123456789000000000000000n, 18];
// If no digits are provided, the digits correspond to the decimals
dnum.format(amount); // 123,456.789
// options.digits
dnum.format(amount, { digits: 2 }); // 123,456.79
dnum.format(amount, 2); // (alias)
// options.compact
dnum.format(amount, { compact: true }); // 123K
// options.trailingZeros
dnum.format(amount, { digits: 6, trailingZeros: true }); // 123,456.789000from(valueToParse, decimals)
Parse a value and convert it into a Dnum.
| Name | Description | Type |
|---|---|---|
valueToParse |
Value to convert into a Dnum |
Numberish |
decimals |
Number of decimals (or true for auto) |
number | true |
| returns | Converted value | Dnum |
Example
// Parses a number expressed as a string or number
let amount = dnum.from("17.30624", 18);
// amount equals [17306240000000000000n, 18]add(value1, value2, decimals)
Adds two values together, regardless of their decimals. decimals correspond to the decimals desired in the result.
| Name | Description | Type |
|---|---|---|
value1 |
First value to add | Numberish |
value2 |
Second value to add | Numberish |
decimals (optional) |
Result decimals (defaults to value1 decimals) | number |
| returns | Result | Dnum |
subtract(value1, value2, decimals)
Subtract a value from another one, regardless of their decimals. decimals correspond to the decimals desired in the result.
| Name | Description | Type |
|---|---|---|
value1 |
First value to add | Numberish |
value2 |
Second value to add | Numberish |
decimals (optional) |
Result decimals (defaults to value1 decimals) | number |
| returns | Result | Dnum |
Alias: sub()
multiply(value1, value2, decimals)
Multiply two values together, regardless of their decimals. decimals correspond to the decimals desired in the result.
| Name | Description | Type |
|---|---|---|
value1 |
First value to multiply | Numberish |
value2 |
Second value to multiply | Numberish |
decimals (optional) |
Result decimals (defaults to value1 decimals) | number |
| returns | Result | Dnum |
Alias: mul()
Example
let ethPriceUsd = [100000n, 2]; // 1000 USD
let tokenPriceEth = [570000000000000000, 18]; // 0.57 ETH
let tokenPriceUsd = dnum.multiply(tokenPriceEth, ethPriceUsd, 2); // 570 USD
// tokenPriceUsd equals [57000, 2]divide(value1, value2, decimals)
Divide a value by another one, regardless of their decimals. decimals correspond to the decimals desired in the result.
| Name | Description | Type |
|---|---|---|
value1 |
Dividend | Numberish |
value2 |
Divisor | Numberish |
decimals (optional) |
Result decimals (defaults to value1 decimals) | number |
| returns | Result value | Dnum |
Alias: div()
Example
let ethPriceUsd = [100000n, 2]; // 1000 USD
let tokenPriceUsd = [57000, 2]; // 570 USD
let tokenPriceEth = dnum.divide(tokenPriceUsd, ethPriceUsd, 18); // 0.57 ETH
// tokenPriceEth equals [570000000000000000, 18]toJSON(value)
Converts the Dnum data structure into a JSON-compatible string. This function is provided because JSON.stringify() doesn’t work with BigInt data types.
| Name | Description | Type |
|---|---|---|
value |
The number to convert into a JSON | Dnum |
| returns | Result value | string |
let json = toJSON([123456789000000000000n, 18]);
// json === "[\"123456789000000000000\", 18]";fromJSON(value)
Converts the string resulting from toJSON() back into a Dnum.
| Name | Description | Type |
|---|---|---|
value |
The string value to convert back into a Dnum |
string |
| returns | Result value | Dnum |
let dnum = fromJSON("[\"123456789000000000000\", 18]");
// dnum === [123456789000000000000n, 18]setDecimals(value, decimals)
Return a new Dnum with a different amount of decimals. The value will reflect this change so that the represented number stays the same.
| Name | Description | Type |
|---|---|---|
value |
The number from which decimals will be changed | Dnum |
decimals |
New number of decimals | number |
| returns | Result value | Dnum |
Note: from(value, decimals) can also be used instead.
Tree shaking
To make use of tree shaking, named exports are also provided:
import { format, from } from "dnum";Acknowledgements
- ethers, in particular its
parseFixed()function. - token-amount which was an attempt at solving a similar problem.