math: Change Decimal.multiply to operate on Uint classes
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Decimal } from "./decimal";
|
||||
import { Uint32, Uint53, Uint64 } from "./integers";
|
||||
|
||||
describe("Decimal", () => {
|
||||
describe("fromAtomics", () => {
|
||||
@@ -212,27 +213,24 @@ describe("Decimal", () => {
|
||||
});
|
||||
|
||||
describe("multiply", () => {
|
||||
it("returns correct values", () => {
|
||||
it("returns correct values for Uint32", () => {
|
||||
const zero = Decimal.fromUserInput("0", 5);
|
||||
expect(zero.multiply(Decimal.fromUserInput("0", 5)).toString()).toEqual("0");
|
||||
expect(zero.multiply(Decimal.fromUserInput("1", 5)).toString()).toEqual("0");
|
||||
expect(zero.multiply(Decimal.fromUserInput("2", 5)).toString()).toEqual("0");
|
||||
expect(zero.multiply(Decimal.fromUserInput("2.8", 5)).toString()).toEqual("0");
|
||||
expect(zero.multiply(Decimal.fromUserInput("0.12345", 5)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint32(0)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint32(1)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint32(2)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint32(4294967295)).toString()).toEqual("0");
|
||||
|
||||
const one = Decimal.fromUserInput("1", 5);
|
||||
expect(one.multiply(Decimal.fromUserInput("0", 5)).toString()).toEqual("0");
|
||||
expect(one.multiply(Decimal.fromUserInput("1", 5)).toString()).toEqual("1");
|
||||
expect(one.multiply(Decimal.fromUserInput("2", 5)).toString()).toEqual("2");
|
||||
expect(one.multiply(Decimal.fromUserInput("2.8", 5)).toString()).toEqual("2.8");
|
||||
expect(one.multiply(Decimal.fromUserInput("0.12345", 5)).toString()).toEqual("0.12345");
|
||||
expect(one.multiply(new Uint32(0)).toString()).toEqual("0");
|
||||
expect(one.multiply(new Uint32(1)).toString()).toEqual("1");
|
||||
expect(one.multiply(new Uint32(2)).toString()).toEqual("2");
|
||||
expect(one.multiply(new Uint32(4294967295)).toString()).toEqual("4294967295");
|
||||
|
||||
const oneDotFive = Decimal.fromUserInput("1.5", 5);
|
||||
expect(oneDotFive.multiply(Decimal.fromUserInput("0", 5)).toString()).toEqual("0");
|
||||
expect(oneDotFive.multiply(Decimal.fromUserInput("1", 5)).toString()).toEqual("1.5");
|
||||
expect(oneDotFive.multiply(Decimal.fromUserInput("2", 5)).toString()).toEqual("3");
|
||||
expect(oneDotFive.multiply(Decimal.fromUserInput("2.8", 5)).toString()).toEqual("4.2");
|
||||
expect(oneDotFive.multiply(Decimal.fromUserInput("0.12345", 5)).toString()).toEqual("0.18517");
|
||||
expect(oneDotFive.multiply(new Uint32(0)).toString()).toEqual("0");
|
||||
expect(oneDotFive.multiply(new Uint32(1)).toString()).toEqual("1.5");
|
||||
expect(oneDotFive.multiply(new Uint32(2)).toString()).toEqual("3");
|
||||
expect(oneDotFive.multiply(new Uint32(4294967295)).toString()).toEqual("6442450942.5");
|
||||
|
||||
// original value remain unchanged
|
||||
expect(zero.toString()).toEqual("0");
|
||||
@@ -240,15 +238,58 @@ describe("Decimal", () => {
|
||||
expect(oneDotFive.toString()).toEqual("1.5");
|
||||
});
|
||||
|
||||
it("throws for different fractional digits", () => {
|
||||
it("returns correct values for Uint53", () => {
|
||||
const zero = Decimal.fromUserInput("0", 5);
|
||||
expect(() => zero.multiply(Decimal.fromUserInput("1", 1))).toThrowError(/do not match/i);
|
||||
expect(() => zero.multiply(Decimal.fromUserInput("1", 2))).toThrowError(/do not match/i);
|
||||
expect(() => zero.multiply(Decimal.fromUserInput("1", 3))).toThrowError(/do not match/i);
|
||||
expect(() => zero.multiply(Decimal.fromUserInput("1", 4))).toThrowError(/do not match/i);
|
||||
expect(zero.multiply(new Uint53(0)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint53(1)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint53(2)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint53(9007199254740991)).toString()).toEqual("0");
|
||||
|
||||
expect(() => zero.multiply(Decimal.fromUserInput("1", 6))).toThrowError(/do not match/i);
|
||||
expect(() => zero.multiply(Decimal.fromUserInput("1", 7))).toThrowError(/do not match/i);
|
||||
const one = Decimal.fromUserInput("1", 5);
|
||||
expect(one.multiply(new Uint53(0)).toString()).toEqual("0");
|
||||
expect(one.multiply(new Uint53(1)).toString()).toEqual("1");
|
||||
expect(one.multiply(new Uint53(2)).toString()).toEqual("2");
|
||||
expect(one.multiply(new Uint53(9007199254740991)).toString()).toEqual("9007199254740991");
|
||||
|
||||
const oneDotFive = Decimal.fromUserInput("1.5", 5);
|
||||
expect(oneDotFive.multiply(new Uint53(0)).toString()).toEqual("0");
|
||||
expect(oneDotFive.multiply(new Uint53(1)).toString()).toEqual("1.5");
|
||||
expect(oneDotFive.multiply(new Uint53(2)).toString()).toEqual("3");
|
||||
expect(oneDotFive.multiply(new Uint53(9007199254740991)).toString()).toEqual("13510798882111486.5");
|
||||
|
||||
// original value remain unchanged
|
||||
expect(zero.toString()).toEqual("0");
|
||||
expect(one.toString()).toEqual("1");
|
||||
expect(oneDotFive.toString()).toEqual("1.5");
|
||||
});
|
||||
|
||||
it("returns correct values for Uint64", () => {
|
||||
const zero = Decimal.fromUserInput("0", 5);
|
||||
expect(zero.multiply(Uint64.fromString("0")).toString()).toEqual("0");
|
||||
expect(zero.multiply(Uint64.fromString("1")).toString()).toEqual("0");
|
||||
expect(zero.multiply(Uint64.fromString("2")).toString()).toEqual("0");
|
||||
expect(zero.multiply(Uint64.fromString("18446744073709551615")).toString()).toEqual("0");
|
||||
|
||||
const one = Decimal.fromUserInput("1", 5);
|
||||
expect(one.multiply(Uint64.fromString("0")).toString()).toEqual("0");
|
||||
expect(one.multiply(Uint64.fromString("1")).toString()).toEqual("1");
|
||||
expect(one.multiply(Uint64.fromString("2")).toString()).toEqual("2");
|
||||
expect(one.multiply(Uint64.fromString("18446744073709551615")).toString()).toEqual(
|
||||
"18446744073709551615",
|
||||
);
|
||||
|
||||
const oneDotFive = Decimal.fromUserInput("1.5", 5);
|
||||
expect(oneDotFive.multiply(Uint64.fromString("0")).toString()).toEqual("0");
|
||||
expect(oneDotFive.multiply(Uint64.fromString("1")).toString()).toEqual("1.5");
|
||||
expect(oneDotFive.multiply(Uint64.fromString("2")).toString()).toEqual("3");
|
||||
expect(oneDotFive.multiply(Uint64.fromString("18446744073709551615")).toString()).toEqual(
|
||||
"27670116110564327422.5",
|
||||
);
|
||||
|
||||
// original value remain unchanged
|
||||
expect(zero.toString()).toEqual("0");
|
||||
expect(one.toString()).toEqual("1");
|
||||
expect(oneDotFive.toString()).toEqual("1.5");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import BN from "bn.js";
|
||||
|
||||
import { Uint32, Uint53, Uint64 } from "./integers";
|
||||
|
||||
// Too large values lead to massive memory usage. Limit to something sensible.
|
||||
// The largest value we need is 18 (Ether).
|
||||
const maxFractionalDigits = 100;
|
||||
@@ -127,12 +129,10 @@ export class Decimal {
|
||||
/**
|
||||
* a.multiply(b) returns a*b.
|
||||
*
|
||||
* Both values need to have the same fractional digits.
|
||||
* We only allow multiplication by unsigned integers to avoid rounding errors.
|
||||
*/
|
||||
public multiply(b: Decimal): Decimal {
|
||||
if (this.fractionalDigits !== b.fractionalDigits) throw new Error("Fractional digits do not match");
|
||||
const factor = new BN(10).pow(new BN(this.data.fractionalDigits));
|
||||
const product = this.data.atomics.mul(new BN(b.atomics)).div(factor);
|
||||
public multiply(b: Uint32 | Uint53 | Uint64): Decimal {
|
||||
const product = this.data.atomics.mul(new BN(b.toString()));
|
||||
return new Decimal(product.toString(), this.fractionalDigits);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user