From d116a1a29ada33c9e860b6761efe18cf43668c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cuesta=20Ca=C3=B1ada?= Date: Wed, 3 Jun 2020 15:49:21 +0100 Subject: [PATCH 1/2] Implemented rounding division. --- contracts/math/DecimalMath.sol | 33 +++++++++++++++++++++ contracts/test/math/DecimalMathMock.sol | 25 ++++++++++++++++ test/math/DecimalMath.test.ts | 38 +++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/contracts/math/DecimalMath.sol b/contracts/math/DecimalMath.sol index cdb9213..77e7e5c 100644 --- a/contracts/math/DecimalMath.sol +++ b/contracts/math/DecimalMath.sol @@ -82,4 +82,37 @@ library DecimalMath { { return x.mul(int(unit(decimals))).div(y); } + + /// @dev Divides x between y, rounding to the closes representable number. + /// Assumes x and y are both fixed point with 18 digits. + function divdr(uint256 x, uint256 y) internal pure returns (uint256) { + return divdr(x, y, 18); + } + + /// @dev Divides x between y, rounding to the closes representable number. + /// Assumes x and y are both fixed point with 18 digits. + function divdr(int256 x, int256 y) internal pure returns (int256) { + return divdr(x, y, 18); + } + + /// @dev Divides x between y, rounding to the closes representable number. + /// Assumes x and y are both fixed point with `decimals` digits. + function divdr(uint256 x, uint256 y, uint8 decimals) + internal pure returns (uint256) + { + uint256 z = x.mul(unit(decimals + 1)).div(y); + if (z % 10 > 5) return z / 10 + 1; + else return z / 10; + } + + /// @dev Divides x between y, rounding to the closes representable number. + /// Assumes x and y are both fixed point with `decimals` digits. + function divdr(int256 x, int256 y, uint8 decimals) + internal pure returns (int256) + { + int256 z = x.mul(int(unit(decimals + 1))).div(y); + if (z % 10 > 5) return z / 10 + 1; + else if (z % 10 < -5) return z / 10 - 1; + else return z / 10; + } } diff --git a/contracts/test/math/DecimalMathMock.sol b/contracts/test/math/DecimalMathMock.sol index 30fbd81..9930b52 100644 --- a/contracts/test/math/DecimalMathMock.sol +++ b/contracts/test/math/DecimalMathMock.sol @@ -66,4 +66,29 @@ contract DecimalMathMock { { return x.divd(y, decimals); } + + function divdr(uint256 x, uint256 y) + public virtual pure returns (uint256) + { + return x.divdr(y); + } + + function divdrInt(int256 x, int256 y) + public virtual pure returns (int256) + { + return x.divdr(y); + } + + function divdr2(uint256 x, uint256 y, uint8 decimals) + public virtual pure returns (uint256) + { + return x.divdr(y, decimals); + } + + function divdr2Int(int256 x, int256 y, uint8 decimals) + public virtual pure returns (int256) + { + return x.divdr(y, decimals); + } + } diff --git a/test/math/DecimalMath.test.ts b/test/math/DecimalMath.test.ts index 86c48b4..ba840b4 100644 --- a/test/math/DecimalMath.test.ts +++ b/test/math/DecimalMath.test.ts @@ -18,6 +18,7 @@ contract('DecimalMath', () => { let decimal1_18: BN; let decimal2_18: BN; let decimal3_18: BN; + let decimal4_18: BN; let decimal6_18: BN; let decimal1_16: BN; let decimal2_16: BN; @@ -30,6 +31,7 @@ contract('DecimalMath', () => { decimal1_18 = new BN(await tokenMath.unit(decimals18.toString())); decimal2_18 = decimal1_18.mul(new BN('2')); decimal3_18 = decimal1_18.mul(new BN('3')); + decimal4_18 = decimal1_18.mul(new BN('4')); decimal6_18 = decimal1_18.mul(new BN('6')); decimal1_16 = new BN(await tokenMath.unit(decimals16.toString())); decimal2_16 = decimal1_16.mul(new BN('2')); @@ -128,4 +130,40 @@ contract('DecimalMath', () => { decimals16.toString(), )).should.be.bignumber.equal(decimal2_16.mul(minus1)); }); + + /** + * @test {DecimalMath#divdr()} + */ + it('divides decimal values, rounding away from zero.', async () => { + BN(await tokenMath.divdr( + decimal4_18.toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal(new BN('666666666666666667')); + BN(await tokenMath.divdrInt( + decimal4_18.toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal(new BN('666666666666666667')); + BN(await tokenMath.divdrInt( + decimal4_18.mul(minus1).toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal((new BN('666666666666666667')).mul(minus1)); + }); + + /** + * @test {DecimalMath#divdr()} + */ + it('divides decimal values, rounding towards zero.', async () => { + BN(await tokenMath.divdr( + decimal2_18.toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal(new BN('333333333333333333')); + BN(await tokenMath.divdrInt( + decimal2_18.toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal(new BN('333333333333333333')); + BN(await tokenMath.divdrInt( + decimal2_18.mul(minus1).toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal((new BN('333333333333333333')).mul(minus1)); + }); }); \ No newline at end of file From a2bff5e73feed53b226ba411951d64a885fb88e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cuesta=20Ca=C3=B1ada?= Date: Fri, 5 Jun 2020 10:51:58 +0100 Subject: [PATCH 2/2] Implemented `divdrup` --- contracts/math/DecimalMath.sol | 39 +++++++++++++++++++++++-- contracts/test/math/DecimalMathMock.sol | 23 +++++++++++++++ test/math/DecimalMath.test.ts | 22 ++++++++++++-- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/contracts/math/DecimalMath.sol b/contracts/math/DecimalMath.sol index 77e7e5c..6912784 100644 --- a/contracts/math/DecimalMath.sol +++ b/contracts/math/DecimalMath.sol @@ -95,7 +95,7 @@ library DecimalMath { return divdr(x, y, 18); } - /// @dev Divides x between y, rounding to the closes representable number. + /// @dev Divides x between y, rounding to the closest representable number. /// Assumes x and y are both fixed point with `decimals` digits. function divdr(uint256 x, uint256 y, uint8 decimals) internal pure returns (uint256) @@ -105,14 +105,47 @@ library DecimalMath { else return z / 10; } - /// @dev Divides x between y, rounding to the closes representable number. + /// @dev Divides x between y, rounding to the closest representable number. /// Assumes x and y are both fixed point with `decimals` digits. function divdr(int256 x, int256 y, uint8 decimals) internal pure returns (int256) { - int256 z = x.mul(int(unit(decimals + 1))).div(y); + int256 z = x.mul(int256(unit(decimals + 1))).div(y); if (z % 10 > 5) return z / 10 + 1; else if (z % 10 < -5) return z / 10 - 1; else return z / 10; } + + /// @dev Divides x between y, rounding to the closes representable number. + /// Assumes x and y are both fixed point with 18 digits. + function divdrup(uint256 x, uint256 y) internal pure returns (uint256) { + return divdrup(x, y, 18); + } + + /// @dev Divides x between y, rounding to the closes representable number. + /// Assumes x and y are both fixed point with 18 digits. + function divdrup(int256 x, int256 y) internal pure returns (int256) { + return divdrup(x, y, 18); + } + + /// @dev Divides x between y, rounding to the closest representable number. + /// Assumes x and y are both fixed point with `decimals` digits. + function divdrup(uint256 x, uint256 y, uint8 decimals) + internal pure returns (uint256) + { + uint256 z = x.mul(unit(decimals + 1)).div(y); + if (z % 10 > 0) return z / 10 + 1; + else return z / 10; + } + + /// @dev Divides x between y, rounding to the closest representable number. + /// Assumes x and y are both fixed point with `decimals` digits. + function divdrup(int256 x, int256 y, uint8 decimals) + internal pure returns (int256) + { + int256 z = x.mul(int256(unit(decimals + 1))).div(y); + if (z % 10 > 0) return z / 10 + 1; + else if (z % 10 < 0) return z / 10 - 1; + else return z / 10; + } } diff --git a/contracts/test/math/DecimalMathMock.sol b/contracts/test/math/DecimalMathMock.sol index 9930b52..cfdcef6 100644 --- a/contracts/test/math/DecimalMathMock.sol +++ b/contracts/test/math/DecimalMathMock.sol @@ -91,4 +91,27 @@ contract DecimalMathMock { return x.divdr(y, decimals); } + function divdrup(uint256 x, uint256 y) + public virtual pure returns (uint256) + { + return x.divdrup(y); + } + + function divdrupInt(int256 x, int256 y) + public virtual pure returns (int256) + { + return x.divdrup(y); + } + + function divdrup2(uint256 x, uint256 y, uint8 decimals) + public virtual pure returns (uint256) + { + return x.divdrup(y, decimals); + } + + function divdrup2Int(int256 x, int256 y, uint8 decimals) + public virtual pure returns (int256) + { + return x.divdrup(y, decimals); + } } diff --git a/test/math/DecimalMath.test.ts b/test/math/DecimalMath.test.ts index ba840b4..3cbfd87 100644 --- a/test/math/DecimalMath.test.ts +++ b/test/math/DecimalMath.test.ts @@ -134,7 +134,7 @@ contract('DecimalMath', () => { /** * @test {DecimalMath#divdr()} */ - it('divides decimal values, rounding away from zero.', async () => { + it('divides decimal values, rounding away from zero to the closest representable number.', async () => { BN(await tokenMath.divdr( decimal4_18.toString(), decimal6_18.toString(), @@ -152,7 +152,7 @@ contract('DecimalMath', () => { /** * @test {DecimalMath#divdr()} */ - it('divides decimal values, rounding towards zero.', async () => { + it('divides decimal values, rounding towards zero to the closest representable number.', async () => { BN(await tokenMath.divdr( decimal2_18.toString(), decimal6_18.toString(), @@ -166,4 +166,22 @@ contract('DecimalMath', () => { decimal6_18.toString(), )).should.be.bignumber.equal((new BN('333333333333333333')).mul(minus1)); }); + + /** + * @test {DecimalMath#divdr()} + */ + it('divides decimal values, rounding towards zero to the closest representable number.', async () => { + BN(await tokenMath.divdrup( + decimal2_18.toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal(new BN('333333333333333334')); + BN(await tokenMath.divdrupInt( + decimal2_18.toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal(new BN('333333333333333334')); + BN(await tokenMath.divdrupInt( + decimal2_18.mul(minus1).toString(), + decimal6_18.toString(), + )).should.be.bignumber.equal((new BN('333333333333333334')).mul(minus1)); + }); }); \ No newline at end of file