From f6db5c1f309e44a057f20ef83b76fc81ddd7e21b Mon Sep 17 00:00:00 2001 From: barakman Date: Wed, 24 Nov 2021 14:09:05 +0100 Subject: [PATCH] A function which returns the absolute value of a signed value (#2984) * Add a function which returns the absolute (and obviously unsigned) value of a signed value. * add changelog entry and fix lint Co-authored-by: Hadrien Croubois --- CHANGELOG.md | 1 + contracts/mocks/MathMock.sol | 4 ++++ contracts/utils/math/Math.sol | 10 ++++++++++ test/utils/math/Math.test.js | 18 +++++++++++++++++- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6a8d4e0f..009a2fa4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * `GovernorTimelockControl`: improve the `state()` function to have it reflect cases where a proposal has been canceled directly on the timelock. ([#2977](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2977)) +* `Math`: add a `abs(int256)` method that returns the unsigned absolute value of a signed value. ([#2984](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2984)) ## Unreleased diff --git a/contracts/mocks/MathMock.sol b/contracts/mocks/MathMock.sol index c651b6bb1..f6c11acb4 100644 --- a/contracts/mocks/MathMock.sol +++ b/contracts/mocks/MathMock.sol @@ -20,4 +20,8 @@ contract MathMock { function ceilDiv(uint256 a, uint256 b) public pure returns (uint256) { return Math.ceilDiv(a, b); } + + function abs(int256 n) public pure returns (uint256) { + return Math.abs(n); + } } diff --git a/contracts/utils/math/Math.sol b/contracts/utils/math/Math.sol index b31bca303..52944c2ee 100644 --- a/contracts/utils/math/Math.sol +++ b/contracts/utils/math/Math.sol @@ -40,4 +40,14 @@ library Math { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } + + /** + * @dev Returns the absolute unsigned value of a signed value. + */ + function abs(int256 n) internal pure returns (uint256) { + unchecked { + // must be unchecked in order to support `n = type(int256).min` + return uint256(n >= 0 ? n : -n); + } + } } diff --git a/test/utils/math/Math.test.js b/test/utils/math/Math.test.js index 7e194dec7..eff64b720 100644 --- a/test/utils/math/Math.test.js +++ b/test/utils/math/Math.test.js @@ -1,6 +1,6 @@ const { BN, constants } = require('@openzeppelin/test-helpers'); const { expect } = require('chai'); -const { MAX_UINT256 } = constants; +const { MAX_UINT256, MAX_INT256, MIN_INT256 } = constants; const MathMock = artifacts.require('MathMock'); @@ -85,4 +85,20 @@ contract('Math', function (accounts) { expect(await this.math.ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(MAX_UINT256); }); }); + + describe('abs', function () { + for (const n of [ + MIN_INT256, + MIN_INT256.addn(1), + new BN('-1'), + new BN('0'), + new BN('1'), + MAX_INT256.subn(1), + MAX_INT256, + ]) { + it(`correctly computes the absolute value of ${n}`, async function () { + expect(await this.math.abs(n)).to.be.bignumber.equal(n.abs()); + }); + } + }); });