From 198014ebdf0bc37ee5bcecb2914fcdeb71351c31 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 1 Feb 2025 08:31:29 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Saturating=20(#1334)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/utils/fixedpointmathlib.md | 33 +++++++++++++++++++++++++++++++++ src/utils/FixedPointMathLib.sol | 10 +++++++++- test/FixedPointMathLib.t.sol | 10 +++++----- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/docs/utils/fixedpointmathlib.md b/docs/utils/fixedpointmathlib.md index e58d0a6f08..b9aaeb31d1 100644 --- a/docs/utils/fixedpointmathlib.md +++ b/docs/utils/fixedpointmathlib.md @@ -394,8 +394,41 @@ function zeroFloorSub(uint256 x, uint256 y) returns (uint256 z) ``` +Returns `max(0, x - y)`. Alias for `saturatingSub`. + +### saturatingSub(uint256,uint256) + +```solidity +function saturatingSub(uint256 x, uint256 y) + internal + pure + returns (uint256 z) +``` + Returns `max(0, x - y)`. +### saturatingAdd(uint256,uint256) + +```solidity +function saturatingAdd(uint256 x, uint256 y) + internal + pure + returns (uint256 z) +``` + +Returns `min(2 ** 256 - 1, x + y)`. + +### saturatingMul(uint256,uint256) + +```solidity +function saturatingMul(uint256 x, uint256 y) + internal + pure + returns (uint256 z) +``` + +Returns `min(2 ** 256 - 1, x * y)`. + ### ternary(bool,uint256,uint256) ```solidity diff --git a/src/utils/FixedPointMathLib.sol b/src/utils/FixedPointMathLib.sol index 70b0e30239..d44c6e7773 100644 --- a/src/utils/FixedPointMathLib.sol +++ b/src/utils/FixedPointMathLib.sol @@ -653,7 +653,7 @@ library FixedPointMathLib { } } - /// @dev Returns `max(0, x - y)`. Also known as saturating subtraction. + /// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`. function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { @@ -661,6 +661,14 @@ library FixedPointMathLib { } } + /// @dev Returns `max(0, x - y)`. + function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(gt(x, y), sub(x, y)) + } + } + /// @dev Returns `min(2 ** 256 - 1, x + y)`. function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly diff --git a/test/FixedPointMathLib.t.sol b/test/FixedPointMathLib.t.sol index 66c60277f5..be3d77d5b5 100644 --- a/test/FixedPointMathLib.t.sol +++ b/test/FixedPointMathLib.t.sol @@ -2184,11 +2184,11 @@ contract FixedPointMathLibTest is SoladyTest { assert(FixedPointMathLib.saturatingAdd(x, y) == expected); } - function testSaturatingAdd() public { + function testSaturatingAdd() public view { testSaturatingAdd(123, 456); } - function check_SaturatingAddEquivalence(uint256 x, uint256 y) public { + function check_SaturatingAddEquivalence(uint256 x, uint256 y) public view { testSaturatingAdd(x, y); } @@ -2196,18 +2196,18 @@ contract FixedPointMathLibTest is SoladyTest { return x + y; } - function testSaturatingMul(uint256 x, uint256 y) public { + function testSaturatingMul(uint256 x, uint256 y) public view { bytes memory data = abi.encodeWithSignature("mul(uint256,uint256)", x, y); (bool success,) = address(this).staticcall(data); uint256 expected = !success ? type(uint256).max : x * y; assert(FixedPointMathLib.saturatingMul(x, y) == expected); } - function check_SaturatingMulEquivalence(uint256 x, uint256 y) public { + function check_SaturatingMulEquivalence(uint256 x, uint256 y) public view { testSaturatingMul(x, y); } - function testSaturatingMul() public { + function testSaturatingMul() public view { testSaturatingMul(123, 456); }