-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
161 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { expect, test } from 'vitest' | ||
|
||
import { scale, setAlwaysRoundDown } from './scale' | ||
|
||
test('converts number to unit of a given length', () => { | ||
expect(scale(69, 1)).toMatchInlineSnapshot('690n') | ||
expect(scale(13, 5)).toMatchInlineSnapshot('1300000n') | ||
expect(scale(420, 10)).toMatchInlineSnapshot('4200000000000n') | ||
expect(scale(20, 9)).toMatchInlineSnapshot('20000000000n') | ||
expect(scale(40, 18)).toMatchInlineSnapshot('40000000000000000000n') | ||
expect(scale(1.2345, 4)).toMatchInlineSnapshot('12345n') | ||
expect(scale(1.0045, 4)).toMatchInlineSnapshot('10045n') | ||
expect(scale(1.2345000, 4)).toMatchInlineSnapshot('12345n') | ||
expect(scale('6942069420.12345678912345', 18)).toMatchInlineSnapshot('6942069420123456789123450000n') | ||
expect(scale('6942069420.00045678912345', 18)).toMatchInlineSnapshot('6942069420000456789123450000n') | ||
expect(scale('6942123123123069420.1234544444678912345', 50)) | ||
.toMatchInlineSnapshot('694212312312306942012345444446789123450000000000000000000000000000000n') | ||
expect(scale(-69, 1)).toMatchInlineSnapshot('-690n') | ||
expect(scale(-1.2345, 4)).toMatchInlineSnapshot('-12345n') | ||
expect(scale('-6942069420.12345678912345', 18)).toMatchInlineSnapshot('-6942069420123456789123450000n') | ||
expect(scale('-6942123123123069420.1234544444678912345', 50)) | ||
.toMatchInlineSnapshot('-694212312312306942012345444446789123450000000000000000000000000000000n') | ||
}) | ||
|
||
test('decimals === 0', () => { | ||
expect(scale('69.2352112312312451512412341231', 0)).toMatchInlineSnapshot('69n') | ||
expect(scale('69.5952141234124125231523412312', 0)).toMatchInlineSnapshot('70n') | ||
expect(scale('12301000000000000020000', 0)).toMatchInlineSnapshot('12301000000000000020000n') | ||
expect(scale('12301000000000000020000.123', 0)).toMatchInlineSnapshot('12301000000000000020000n') | ||
expect(scale('12301000000000000020000.5', 0)).toMatchInlineSnapshot('12301000000000000020001n') | ||
expect(scale('99999999999999999999999.5', 0)).toMatchInlineSnapshot('100000000000000000000000n') | ||
}) | ||
|
||
test('decimals < fraction length', () => { | ||
expect(scale(69.23521, 0)).toMatchInlineSnapshot('69n') | ||
expect(scale(69.56789, 0)).toMatchInlineSnapshot('70n') | ||
expect(scale(69.23521, 1)).toMatchInlineSnapshot('692n') | ||
expect(scale(69.23521, 2)).toMatchInlineSnapshot('6924n') | ||
expect(scale(69.23221, 2)).toMatchInlineSnapshot('6923n') | ||
expect(scale(69.23261, 3)).toMatchInlineSnapshot('69233n') | ||
expect(scale(999999.99999, 3)).toMatchInlineSnapshot('1000000000n') | ||
expect(scale(699999.99999, 3)).toMatchInlineSnapshot('700000000n') | ||
expect(scale(699999.98999, 3)).toMatchInlineSnapshot('699999990n') | ||
expect(scale(699959.99999, 3)).toMatchInlineSnapshot('699960000n') | ||
expect(scale(699099.99999, 3)).toMatchInlineSnapshot('699100000n') | ||
expect(scale(100000.000999, 3)).toMatchInlineSnapshot('100000001n') | ||
expect(scale(100000.990999, 3)).toMatchInlineSnapshot('100000991n') | ||
expect(scale(69.00221, 3)).toMatchInlineSnapshot('69002n') | ||
expect(scale('1.0536059576998882', 7)).toMatchInlineSnapshot('10536060n') | ||
expect(scale('1.0053059576998882', 7)).toMatchInlineSnapshot('10053060n') | ||
expect(scale('1.0000000900000000', 7)).toMatchInlineSnapshot('10000001n') | ||
expect(scale('1.0000009900000000', 7)).toMatchInlineSnapshot('10000010n') | ||
expect(scale('1.0000099900000000', 7)).toMatchInlineSnapshot('10000100n') | ||
expect(scale('1.0000092900000000', 7)).toMatchInlineSnapshot('10000093n') | ||
expect(scale('1.5536059576998882', 7)).toMatchInlineSnapshot('15536060n') | ||
expect(scale('1.0536059476998882', 7)).toMatchInlineSnapshot('10536059n') | ||
expect(scale('1.4545454545454545', 7)).toMatchInlineSnapshot('14545455n') | ||
expect(scale('1.1234567891234567', 7)).toMatchInlineSnapshot('11234568n') | ||
expect(scale('1.8989898989898989', 7)).toMatchInlineSnapshot('18989899n') | ||
expect(scale('9.9999999999999999', 7)).toMatchInlineSnapshot('100000000n') | ||
expect(scale('0.0536059576998882', 7)).toMatchInlineSnapshot('536060n') | ||
expect(scale('0.0053059576998882', 7)).toMatchInlineSnapshot('53060n') | ||
expect(scale('0.0000000900000000', 7)).toMatchInlineSnapshot('1n') | ||
expect(scale('0.0000009900000000', 7)).toMatchInlineSnapshot('10n') | ||
expect(scale('0.0000099900000000', 7)).toMatchInlineSnapshot('100n') | ||
expect(scale('0.0000092900000000', 7)).toMatchInlineSnapshot('93n') | ||
expect(scale('0.0999999999999999', 7)).toMatchInlineSnapshot('1000000n') | ||
expect(scale('0.0099999999999999', 7)).toMatchInlineSnapshot('100000n') | ||
expect(scale('0.00000000059', 9)).toMatchInlineSnapshot('1n') | ||
expect(scale('0.0000000003', 9)).toMatchInlineSnapshot('0n') | ||
expect(scale('69.00000000000', 9)).toMatchInlineSnapshot('69000000000n') | ||
expect(scale('69.00000000019', 9)).toMatchInlineSnapshot('69000000000n') | ||
expect(scale('69.00000000059', 9)).toMatchInlineSnapshot('69000000001n') | ||
expect(scale('69.59000000059', 9)).toMatchInlineSnapshot('69590000001n') | ||
expect(scale('69.59000002359', 9)).toMatchInlineSnapshot('69590000024n') | ||
expect(scale('100.00000023', 6)).toMatchInlineSnapshot('100000000n') | ||
}) | ||
|
||
// round down with striping extra decimals | ||
test('decimals > fraction length with always round down', () => { | ||
expect(scale(69.9, 0)).toMatchInlineSnapshot('70n') | ||
expect(scale(69.23521, 0)).toMatchInlineSnapshot('69n') | ||
expect(scale(69.23521, 2)).toMatchInlineSnapshot('6924n') | ||
setAlwaysRoundDown(true) | ||
expect(scale(69.9, 0)).toMatchInlineSnapshot('69n') | ||
expect(scale(69.23521, 2)).toMatchInlineSnapshot('6923n') | ||
setAlwaysRoundDown(true) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
let isAlwaysRoundDown = false | ||
|
||
/** | ||
* Set the default rounding behavior for the scale function. If true, the scale function will always round down. | ||
* If false, the scale function will round off. | ||
* @notice use with caution, as this will affect all calls to the scale function. | ||
* @warning use if you want your entire application to always round down. | ||
*/ | ||
export function setAlwaysRoundDown(value: boolean) { | ||
isAlwaysRoundDown = value | ||
} | ||
|
||
/** | ||
* Multiplies a string representation of a number by a given exponent of base 10 (10exponent). | ||
* @returns A BigInt number | ||
* @example | ||
* scale('112', 18) // 112000000000000000000n | ||
* scale(112, 18) // 112000000000000000000n | ||
* scale(112.5632, 0) // 113n | ||
* scale(112.5632, 0, true) // 112n | ||
*/ | ||
export function scale(value: string | number, decimals: number, roundDown = isAlwaysRoundDown) { | ||
let [integer, fraction = '0'] = `${value}`.split('.') | ||
|
||
const negative = integer.startsWith('-') | ||
if (negative) | ||
integer = integer.slice(1) | ||
|
||
// trim leading zeros. | ||
fraction = fraction.replace(/(0+)$/, '') | ||
|
||
// round off if the fraction is larger than the number of decimals. | ||
if (decimals === 0) { | ||
if (Math.round(Number(`.${fraction}`)) === 1 && !roundDown) | ||
integer = `${BigInt(integer) + BigInt(1)}` | ||
fraction = '' | ||
} | ||
else if (fraction.length > decimals && !roundDown) { | ||
const left = fraction.slice(0, decimals - 1) | ||
const unit = fraction.slice(decimals - 1, decimals) | ||
const right = fraction.slice(decimals) | ||
|
||
const rounded = Math.round(Number(`${unit}.${right}`)) | ||
if (rounded > 9) | ||
fraction = `${BigInt(left) + BigInt(1)}0`.padStart(left.length + 1, '0') | ||
else fraction = `${left}${rounded}` | ||
|
||
if (fraction.length > decimals) { | ||
fraction = fraction.slice(1) | ||
integer = `${BigInt(integer) + BigInt(1)}` | ||
} | ||
|
||
fraction = fraction.slice(0, decimals) | ||
} | ||
else if (fraction.length > decimals && roundDown) { | ||
fraction = fraction.slice(0, decimals) | ||
} | ||
else { | ||
fraction = fraction.padEnd(decimals, '0') | ||
} | ||
|
||
return BigInt(`${negative ? '-' : ''}${integer}${fraction}`) | ||
} | ||
|
||
/** | ||
* wrapper for scale function that returns a string representation of the result. | ||
* @deprecated use scale().toString() instead | ||
*/ | ||
export function scaleToString(value: string | number, decimals: number, roundDown = isAlwaysRoundDown) { | ||
return scale(value, decimals, roundDown).toString() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters