Skip to content

Commit f110a11

Browse files
authored
Implemented rounding division. (#349)
* Implemented rounding towards zero division. * Implemented rounding away from zero division.
1 parent 511accc commit f110a11

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

contracts/math/DecimalMath.sol

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,70 @@ library DecimalMath {
8282
{
8383
return x.mul(int(unit(decimals))).div(y);
8484
}
85+
86+
/// @dev Divides x between y, rounding to the closes representable number.
87+
/// Assumes x and y are both fixed point with 18 digits.
88+
function divdr(uint256 x, uint256 y) internal pure returns (uint256) {
89+
return divdr(x, y, 18);
90+
}
91+
92+
/// @dev Divides x between y, rounding to the closes representable number.
93+
/// Assumes x and y are both fixed point with 18 digits.
94+
function divdr(int256 x, int256 y) internal pure returns (int256) {
95+
return divdr(x, y, 18);
96+
}
97+
98+
/// @dev Divides x between y, rounding to the closest representable number.
99+
/// Assumes x and y are both fixed point with `decimals` digits.
100+
function divdr(uint256 x, uint256 y, uint8 decimals)
101+
internal pure returns (uint256)
102+
{
103+
uint256 z = x.mul(unit(decimals + 1)).div(y);
104+
if (z % 10 > 5) return z / 10 + 1;
105+
else return z / 10;
106+
}
107+
108+
/// @dev Divides x between y, rounding to the closest representable number.
109+
/// Assumes x and y are both fixed point with `decimals` digits.
110+
function divdr(int256 x, int256 y, uint8 decimals)
111+
internal pure returns (int256)
112+
{
113+
int256 z = x.mul(int256(unit(decimals + 1))).div(y);
114+
if (z % 10 > 5) return z / 10 + 1;
115+
else if (z % 10 < -5) return z / 10 - 1;
116+
else return z / 10;
117+
}
118+
119+
/// @dev Divides x between y, rounding to the closes representable number.
120+
/// Assumes x and y are both fixed point with 18 digits.
121+
function divdrup(uint256 x, uint256 y) internal pure returns (uint256) {
122+
return divdrup(x, y, 18);
123+
}
124+
125+
/// @dev Divides x between y, rounding to the closes representable number.
126+
/// Assumes x and y are both fixed point with 18 digits.
127+
function divdrup(int256 x, int256 y) internal pure returns (int256) {
128+
return divdrup(x, y, 18);
129+
}
130+
131+
/// @dev Divides x between y, rounding to the closest representable number.
132+
/// Assumes x and y are both fixed point with `decimals` digits.
133+
function divdrup(uint256 x, uint256 y, uint8 decimals)
134+
internal pure returns (uint256)
135+
{
136+
uint256 z = x.mul(unit(decimals + 1)).div(y);
137+
if (z % 10 > 0) return z / 10 + 1;
138+
else return z / 10;
139+
}
140+
141+
/// @dev Divides x between y, rounding to the closest representable number.
142+
/// Assumes x and y are both fixed point with `decimals` digits.
143+
function divdrup(int256 x, int256 y, uint8 decimals)
144+
internal pure returns (int256)
145+
{
146+
int256 z = x.mul(int256(unit(decimals + 1))).div(y);
147+
if (z % 10 > 0) return z / 10 + 1;
148+
else if (z % 10 < 0) return z / 10 - 1;
149+
else return z / 10;
150+
}
85151
}

contracts/test/math/DecimalMathMock.sol

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,52 @@ contract DecimalMathMock {
6666
{
6767
return x.divd(y, decimals);
6868
}
69+
70+
function divdr(uint256 x, uint256 y)
71+
public virtual pure returns (uint256)
72+
{
73+
return x.divdr(y);
74+
}
75+
76+
function divdrInt(int256 x, int256 y)
77+
public virtual pure returns (int256)
78+
{
79+
return x.divdr(y);
80+
}
81+
82+
function divdr2(uint256 x, uint256 y, uint8 decimals)
83+
public virtual pure returns (uint256)
84+
{
85+
return x.divdr(y, decimals);
86+
}
87+
88+
function divdr2Int(int256 x, int256 y, uint8 decimals)
89+
public virtual pure returns (int256)
90+
{
91+
return x.divdr(y, decimals);
92+
}
93+
94+
function divdrup(uint256 x, uint256 y)
95+
public virtual pure returns (uint256)
96+
{
97+
return x.divdrup(y);
98+
}
99+
100+
function divdrupInt(int256 x, int256 y)
101+
public virtual pure returns (int256)
102+
{
103+
return x.divdrup(y);
104+
}
105+
106+
function divdrup2(uint256 x, uint256 y, uint8 decimals)
107+
public virtual pure returns (uint256)
108+
{
109+
return x.divdrup(y, decimals);
110+
}
111+
112+
function divdrup2Int(int256 x, int256 y, uint8 decimals)
113+
public virtual pure returns (int256)
114+
{
115+
return x.divdrup(y, decimals);
116+
}
69117
}

test/math/DecimalMath.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ contract('DecimalMath', () => {
1818
let decimal1_18: BN;
1919
let decimal2_18: BN;
2020
let decimal3_18: BN;
21+
let decimal4_18: BN;
2122
let decimal6_18: BN;
2223
let decimal1_16: BN;
2324
let decimal2_16: BN;
@@ -30,6 +31,7 @@ contract('DecimalMath', () => {
3031
decimal1_18 = new BN(await tokenMath.unit(decimals18.toString()));
3132
decimal2_18 = decimal1_18.mul(new BN('2'));
3233
decimal3_18 = decimal1_18.mul(new BN('3'));
34+
decimal4_18 = decimal1_18.mul(new BN('4'));
3335
decimal6_18 = decimal1_18.mul(new BN('6'));
3436
decimal1_16 = new BN(await tokenMath.unit(decimals16.toString()));
3537
decimal2_16 = decimal1_16.mul(new BN('2'));
@@ -128,4 +130,58 @@ contract('DecimalMath', () => {
128130
decimals16.toString(),
129131
)).should.be.bignumber.equal(decimal2_16.mul(minus1));
130132
});
133+
134+
/**
135+
* @test {DecimalMath#divdr()}
136+
*/
137+
it('divides decimal values, rounding away from zero to the closest representable number.', async () => {
138+
BN(await tokenMath.divdr(
139+
decimal4_18.toString(),
140+
decimal6_18.toString(),
141+
)).should.be.bignumber.equal(new BN('666666666666666667'));
142+
BN(await tokenMath.divdrInt(
143+
decimal4_18.toString(),
144+
decimal6_18.toString(),
145+
)).should.be.bignumber.equal(new BN('666666666666666667'));
146+
BN(await tokenMath.divdrInt(
147+
decimal4_18.mul(minus1).toString(),
148+
decimal6_18.toString(),
149+
)).should.be.bignumber.equal((new BN('666666666666666667')).mul(minus1));
150+
});
151+
152+
/**
153+
* @test {DecimalMath#divdr()}
154+
*/
155+
it('divides decimal values, rounding towards zero to the closest representable number.', async () => {
156+
BN(await tokenMath.divdr(
157+
decimal2_18.toString(),
158+
decimal6_18.toString(),
159+
)).should.be.bignumber.equal(new BN('333333333333333333'));
160+
BN(await tokenMath.divdrInt(
161+
decimal2_18.toString(),
162+
decimal6_18.toString(),
163+
)).should.be.bignumber.equal(new BN('333333333333333333'));
164+
BN(await tokenMath.divdrInt(
165+
decimal2_18.mul(minus1).toString(),
166+
decimal6_18.toString(),
167+
)).should.be.bignumber.equal((new BN('333333333333333333')).mul(minus1));
168+
});
169+
170+
/**
171+
* @test {DecimalMath#divdr()}
172+
*/
173+
it('divides decimal values, rounding towards zero to the closest representable number.', async () => {
174+
BN(await tokenMath.divdrup(
175+
decimal2_18.toString(),
176+
decimal6_18.toString(),
177+
)).should.be.bignumber.equal(new BN('333333333333333334'));
178+
BN(await tokenMath.divdrupInt(
179+
decimal2_18.toString(),
180+
decimal6_18.toString(),
181+
)).should.be.bignumber.equal(new BN('333333333333333334'));
182+
BN(await tokenMath.divdrupInt(
183+
decimal2_18.mul(minus1).toString(),
184+
decimal6_18.toString(),
185+
)).should.be.bignumber.equal((new BN('333333333333333334')).mul(minus1));
186+
});
131187
});

0 commit comments

Comments
 (0)