feat(libraries): add full-precision multiply-divide math
This commit is contained in:
1 parent
d83d3c1e96
commit
d3d2a26bd1
1 file changed
+69
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.36;
|
||||
|
||||
/// @title 512 bit multiply then divide without intermediate overflow.
|
||||
library FullMath {
|
||||
function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
|
||||
unchecked {
|
||||
uint256 prod0;
|
||||
uint256 prod1;
|
||||
assembly {
|
||||
let mm := mulmod(a, b, not(0))
|
||||
prod0 := mul(a, b)
|
||||
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
|
||||
}
|
||||
|
||||
if (prod1 == 0) {
|
||||
require(denominator > 0, "FullMath: DIV_ZERO");
|
||||
assembly {
|
||||
result := div(prod0, denominator)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
require(denominator > prod1, "FullMath: OVERFLOW");
|
||||
|
||||
uint256 remainder;
|
||||
assembly {
|
||||
remainder := mulmod(a, b, denominator)
|
||||
}
|
||||
assembly {
|
||||
prod1 := sub(prod1, gt(remainder, prod0))
|
||||
prod0 := sub(prod0, remainder)
|
||||
}
|
||||
|
||||
uint256 twos = (0 - denominator) & denominator;
|
||||
assembly {
|
||||
denominator := div(denominator, twos)
|
||||
}
|
||||
assembly {
|
||||
prod0 := div(prod0, twos)
|
||||
}
|
||||
assembly {
|
||||
twos := add(div(sub(0, twos), twos), 1)
|
||||
}
|
||||
prod0 |= prod1 * twos;
|
||||
|
||||
uint256 inv = (3 * denominator) ^ 2;
|
||||
inv *= 2 - denominator * inv;
|
||||
inv *= 2 - denominator * inv;
|
||||
inv *= 2 - denominator * inv;
|
||||
inv *= 2 - denominator * inv;
|
||||
inv *= 2 - denominator * inv;
|
||||
inv *= 2 - denominator * inv;
|
||||
|
||||
result = prod0 * inv;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
|
||||
unchecked {
|
||||
result = mulDiv(a, b, denominator);
|
||||
if (mulmod(a, b, denominator) > 0) {
|
||||
require(result < type(uint256).max, "FullMath: ROUND_OVERFLOW");
|
||||
result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user