diff --git a/packages/bignumber/src.ts/fixednumber.ts b/packages/bignumber/src.ts/fixednumber.ts index 54968453fc..9500457a54 100644 --- a/packages/bignumber/src.ts/fixednumber.ts +++ b/packages/bignumber/src.ts/fixednumber.ts @@ -240,29 +240,56 @@ export class FixedNumber { return FixedNumber.fromValue(a.mul(this.format._multiplier).div(b), this.format.decimals, this.format); } + floor(): FixedNumber { + let comps = this.toString().split("."); + + let result = FixedNumber.from(comps[0], this.format); + + const hasFraction = !comps[1].match(/^(0*)$/); + if (this.isNegative() && hasFraction) { + result = result.subUnsafe(ONE); + } + + return result; + } + + ceiling(): FixedNumber { + let comps = this.toString().split("."); + + let result = FixedNumber.from(comps[0], this.format); + + const hasFraction = !comps[1].match(/^(0*)$/); + if (!this.isNegative() && hasFraction) { + result = result.addUnsafe(ONE); + } + + return result; + } + // @TODO: Support other rounding algorithms round(decimals?: number): FixedNumber { if (decimals == null) { decimals = 0; } + + // If we are already in range, we're done + let comps = this.toString().split("."); + if (decimals < 0 || decimals > 80 || (decimals % 1)) { logger.throwArgumentError("invalid decimal count", "decimals", decimals); } - // If we are already in range, we're done - let comps = this.toString().split("."); if (comps[1].length <= decimals) { return this; } - // Bump the value up by the 0.00...0005 - const bump = "0." + zeros.substring(0, decimals) + "5"; - comps = this.addUnsafe(FixedNumber.fromString(bump, this.format))._value.split("."); - - // Now it is safe to truncate - return FixedNumber.fromString(comps[0] + "." + comps[1].substring(0, decimals)); + const factor = FixedNumber.from("1" + zeros.substring(0, decimals)); + return this.mulUnsafe(factor).addUnsafe(BUMP).floor().divUnsafe(factor); } isZero(): boolean { return (this._value === "0.0"); } + isNegative(): boolean { + return (this._value[0] === "-"); + } toString(): string { return this._value; } @@ -361,3 +388,6 @@ export class FixedNumber { return !!(value && value._isFixedNumber); } } + +const ONE = FixedNumber.from(1); +const BUMP = FixedNumber.from("0.5"); diff --git a/packages/tests/src.ts/test-utils.ts b/packages/tests/src.ts/test-utils.ts index 46ce6817c2..2e77544afb 100644 --- a/packages/tests/src.ts/test-utils.ts +++ b/packages/tests/src.ts/test-utils.ts @@ -623,6 +623,80 @@ describe("BigNumber", function() { }); + +describe("FixedNumber", function() { + { + const Tests = [ + { value: "0.0", expected: "0.0" }, + { value: "-0.0", expected: "0.0" }, + + { value: "1.0", expected: "1.0" }, + { value: "1.00", expected: "1.0" }, + { value: "01.00", expected: "1.0" }, + { value: 1, expected: "1.0" }, + + { value: "-1.0", expected: "-1.0" }, + { value: "-1.00", expected: "-1.0" }, + { value: "-01.00", expected: "-1.0" }, + { value: -1, expected: "-1.0" }, + ]; + + Tests.forEach((test) => { + it (`Create from=${ test.value }`, function() { + const value = ethers.FixedNumber.from(test.value); + assert.equal(value.toString(), test.expected); + }); + }); + } + + { + const Tests = [ + { value: "1.0", round: 1, expected: "1.0" }, + { value: "1.4", round: 1, expected: "1.4" }, + { value: "1.4", round: 2, expected: "1.4" }, + { value: "1.4", round: 0, expected: "1.0" }, + { value: "1.5", round: 0, expected: "2.0" }, + { value: "1.6", round: 0, expected: "2.0" }, + + { value: "-1.0", round: 1, expected: "-1.0" }, + { value: "-1.4", round: 1, expected: "-1.4" }, + { value: "-1.4", round: 2, expected: "-1.4" }, + { value: "-1.4", round: 0, expected: "-1.0" }, + { value: "-1.5", round: 0, expected: "-1.0" }, + { value: "-1.6", round: 0, expected: "-2.0" }, + + { value: "1.51", round: 1, expected: "1.5" }, + { value: "1.55", round: 1, expected: "1.6" }, + ]; + + Tests.forEach((test) => { + it (`Rounding value=${ test.value }, decimals=${ test.round }`, function() { + const value = ethers.FixedNumber.from(test.value).round(test.round); + assert.equal(value.toString(), test.expected); + }); + }); + } + + { + const Tests = [ + { value: "1.0", ceiling: "1.0", floor: "1.0" }, + { value: "1.1", ceiling: "2.0", floor: "1.0" }, + { value: "1.9", ceiling: "2.0", floor: "1.0" }, + { value: "-1.0", ceiling: "-1.0", floor: "-1.0" }, + { value: "-1.1", ceiling: "-1.0", floor: "-2.0" }, + { value: "-1.9", ceiling: "-1.0", floor: "-2.0" }, + ]; + + Tests.forEach((test) => { + it (`Clamping value=${ test.value }`, function() { + const value = ethers.FixedNumber.from(test.value); + assert.equal(value.floor().toString(), test.floor); + assert.equal(value.ceiling().toString(), test.ceiling); + }); + }); + } +}); + describe("Logger", function() { const logger = new ethers.utils.Logger("testing/0.0"); @@ -658,7 +732,6 @@ describe("Logger", function() { }); }); - /* describe("Base58 Coder", function() { it("decodes", function() {