Skip to content

Commit

Permalink
perf: speed up "exp2" by simplifying the integer part calculation
Browse files Browse the repository at this point in the history
refactor: remove redundant line in PRBMathSD59x18.exp2
  • Loading branch information
PaulRBerg committed Apr 24, 2021
1 parent 7790007 commit 62021c1
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 13 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Based on v1.0.0 of the library.
| ceil | 82 | 117 | 101 | | ceil | 78 | 78 | 78 |
| div | 431 | 483 | 451 | | div | 205 | 205 | 205 |
| exp | 35 | 3272 | 2507 | | exp | 2065 | 3220 | 2529 |
| exp2 | 60 | 3156 | 2247 | | exp2 | 1975 | 3130 | 2417 |
| exp2 | 63 | 2678 | 2113 | | exp2 | 1784 | 2652 | 2112 |
| floor | 82 | 117 | 101 | | floor | 43 | 43 | 43 |
| frac | 23 | 23 | 23 | | frac | 23 | 23 | 23 |
| gm | 26 | 892 | 690 | | gm | 26 | 893 | 691 |
Expand Down
23 changes: 14 additions & 9 deletions contracts/PRBMathCommon.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ library PRBMathCommon {

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Uses 128.128-bit fixed-point numbers, which is the most efficient way.
/// See https://ethereum.stackexchange.com/a/96594/24693
/// See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 128.128-bit fixed-point number.
/// @return result The result as an unsigned 60x18 decimal fixed-point number.
function exp2(uint256 x) internal pure returns (uint256 result) {
Expand Down Expand Up @@ -91,12 +91,17 @@ library PRBMathCommon {
if (x & 0x20000000000000000 > 0) result = (result * 0x1000000000000000162E42FEFA39EF359) >> 128;
if (x & 0x10000000000000000 > 0) result = (result * 0x10000000000000000B17217F7D1CF79AC) >> 128;

// Multiply the result by the integer part 2^n + 1. We have to shift by one bit extra because we have already divided
// by two when we set the result equal to 0.5 above.
result = result << ((x >> 128) + 1);

// Convert the result to the signed 60.18-decimal fixed-point format.
result = PRBMathCommon.mulDiv(result, SCALE, 2**128);
// We do two things at the same time below:
//
// 1. Multiply the result by 2^n + 1, where 2^n is the integer part and 1 is an extra bit to account
// for the fact that we initially set the result to 0.5 We implement this by subtracting from 127
// instead of 128.
// 2. Convert the result to the unsigned 60.18-decimal fixed-point format.
//
// This works because result * SCALE * 2^ip / 2^127 = result * SCALE / 2^(127 - ip), where ip is the integer
// part and SCALE / 2^128 is what converts the result to our unsigned fixed-point format.
result *= SCALE;
result >>= (127 - (x >> 128));
}
}

Expand Down Expand Up @@ -251,8 +256,8 @@ library PRBMathCommon {
/// Caveats:
/// - The body is purposely left uncommented; see the NatSpec comments in "PRBMathCommon.mulDiv" to understand how this works.
/// - It is assumed that the result can never be type(uint256).max when x and y solve the following two queations:
/// 1) x * y = type(uint256).max * SCALE
/// 2) (x * y) % SCALE >= SCALE / 2
/// 1. x * y = type(uint256).max * SCALE
/// 2. (x * y) % SCALE >= SCALE / 2
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
Expand Down
5 changes: 2 additions & 3 deletions contracts/PRBMathSD59x18.sol
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,15 @@ library PRBMathSD59x18 {
/// @param x The exponent as a signed 59.18-decimal fixed-point number.
/// @return result The result as a signed 59.18-decimal fixed-point number.
function exp2(int256 x) internal pure returns (int256 result) {
// This works because 2^-x = 1/2^x.
// This works because 2^(-x) = 1/2^x.
if (x < 0) {
// 2**59.794705707972522262 is the maximum number whose inverse does not equal zero.
// 2**59.794705707972522262 is the maximum number whose inverse does not turn into zero.
if (x < -59794705707972522261) {
return 0;
}

// Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE.
unchecked { result = 1e36 / exp2(-x); }
return result;
} else {
// 2**128 doesn't fit within the 128.128-bit fixed-point representation.
require(x < 128e18);
Expand Down

0 comments on commit 62021c1

Please sign in to comment.