diff options
author | Florian Dold <florian.dold@gmail.com> | 2018-07-05 02:09:07 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2018-07-05 02:09:07 +0200 |
commit | 075fe28f74c9545a2d2d144a02abb134430d1352 (patch) | |
tree | bd14f82260806d4603adf26121685b376699fdd7 | |
parent | a4edc3b17f27de5866e11d04a1fc9bbd4d657867 (diff) |
avoid floating point imprecision with amounts
-rw-r--r-- | src/amounts.ts | 23 | ||||
-rw-r--r-- | src/types-test.ts | 9 |
2 files changed, 31 insertions, 1 deletions
diff --git a/src/amounts.ts b/src/amounts.ts index 1aefe07e1..1ab00f81d 100644 --- a/src/amounts.ts +++ b/src/amounts.ts @@ -31,6 +31,13 @@ import { Checkable } from "./checkable"; */ export const fractionalBase = 1e8; +/** + * How many digits behind the comma are required to represent the + * fractional value in human readable decimal format? Must match + * lg(fractionalBase) + */ +export const fractionalLength = 8; + /** * Non-negative financial amount. Fractional values are expressed as multiples @@ -282,7 +289,21 @@ export function fromFloat(floatVal: number, currency: string) { * also used in JSON formats. */ export function toString(a: AmountJson) { - return `${a.currency}:${a.value + (a.fraction / fractionalBase)}`; + let s = a.value.toString() + + if (a.fraction) { + s = s + "."; + let n = a.fraction; + for (let i = 0; i < fractionalLength; i++) { + if (!n) { + break; + } + s = s + Math.floor(n / fractionalBase * 10).toString(); + n = (n * 10) % fractionalBase; + } + } + + return `${a.currency}:${s}`; } diff --git a/src/types-test.ts b/src/types-test.ts index d65daeade..626063eba 100644 --- a/src/types-test.ts +++ b/src/types-test.ts @@ -63,6 +63,15 @@ test("amount parsing", (t) => { }); +test("amount stringification", (t) => { + t.is(Amounts.toString(amt(4, 94000000, "TESTKUDOS")), "TESTKUDOS:4.94"); + t.is(Amounts.toString(amt(0, 10000000, "TESTKUDOS")), "TESTKUDOS:0.1"); + t.is(Amounts.toString(amt(0, 1, "TESTKUDOS")), "TESTKUDOS:0.00000001"); + t.is(Amounts.toString(amt(5, 0, "TESTKUDOS")), "TESTKUDOS:5"); + t.pass(); +}); + + test("contract terms validation", (t) => { const c = { H_wire: "123", |