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