From 075fe28f74c9545a2d2d144a02abb134430d1352 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 5 Jul 2018 02:09:07 +0200 Subject: avoid floating point imprecision with amounts --- src/amounts.ts | 23 ++++++++++++++++++++++- src/types-test.ts | 9 +++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'src') 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", -- cgit v1.2.3