Skip to content

Commit

Permalink
improve signed average formula
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx committed Jan 6, 2022
1 parent bf99844 commit 234cfb8
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 9 deletions.
16 changes: 8 additions & 8 deletions contracts/utils/math/SignedMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ library SignedMath {
* towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
if ((b < 0 && a > 0 && -b < a) || (a < 0 && b > 0 && -a < b))
return (a / 2) + (b / 2) + (((a % 2 + b % 2) - 1) / 2);

if ((b < 0 && a > 0 && -b > a) || (a < 0 && b > 0 && -a > b))
return (a / 2) + (b / 2) + (((a % 2 + b % 2) + 1) / 2);

return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
// (a + b) / 2 can overflow, and the unsigned formula doesn't simply translate to signed integers
int256 base = (a & b) + (a ^ b) / 2;
if ((a < 0 && b < 0) || ((a < 0 || b < 0) && (a + b > 0))) {
return base + (a ^ b) % 2;
} else {
return base;
}
}

/**
Expand All @@ -45,4 +45,4 @@ library SignedMath {
int256 r = a % b == 0 ? int256(0) : int256(1);
return z < 0 ? z - r : z + r;
}
}
}
9 changes: 8 additions & 1 deletion test/utils/math/SignedMath.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,56 +85,63 @@ contract('SignedMath', function (accounts) {
expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});

// eslint-disable-next-line max-len
it('is correctly calculated with one even number and one odd signed number and even its greater than odd, both in module', async function () {
const a = new BN('2');
const b = new BN('-1');

expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});

// eslint-disable-next-line max-len
it('is correctly calculated with one odd number and one even signed number and odd its greater than even, both in module', async function () {
const a = new BN('3');
const b = new BN('-2');

expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});


// eslint-disable-next-line max-len
it('is correctly calculated with one even number and one odd signed number and odd its greater than even, both in module', async function () {
const a = new BN('2');
const b = new BN('-3');

expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});

// eslint-disable-next-line max-len
it('is correctly calculated with one odd number and one even signed number and even its greater than odd, both in module', async function () {
const a = new BN('3');
const b = new BN('-4');

expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});

// eslint-disable-next-line max-len
it('is correctly calculated with one even signed number and one odd number and even its greater than odd, both in module', async function () {
const a = new BN('-2');
const b = new BN('1');

expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});

// eslint-disable-next-line max-len
it('is correctly calculated with one odd signed number and one even number and odd its greater than even, both in module', async function () {
const a = new BN('-3');
const b = new BN('2');

expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});

// eslint-disable-next-line max-len
it('is correctly calculated with one even signed number and one odd number and odd its greater than even, both in module', async function () {
const a = new BN('-2');
const b = new BN('3');

expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});

// eslint-disable-next-line max-len
it('is correctly calculated with one odd signed number and one even number and even its greater than odd, both in module', async function () {
const a = new BN('-3');
const b = new BN('4');
Expand Down

0 comments on commit 234cfb8

Please sign in to comment.