avoid floating point imprecision with amounts

This commit is contained in:
Florian Dold 2018-07-05 02:09:07 +02:00
parent a4edc3b17f
commit 075fe28f74
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 31 additions and 1 deletions

View File

@ -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}`;
}

View File

@ -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",