feat(libraries): add full-precision multiply-divide math

This commit is contained in:
dualfroz committed 2026-08-23 14:28:00 +02:00
1 parent d83d3c1e96
commit d3d2a26bd1
1 file changed
+69
+69
View File
@@ -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++;
}
}
}
}