-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add log2, log10 and log256 functions #3670
Changes from 3 commits
8748496
a177655
c3df578
ec96d9d
b8a9d4b
ff5ad8f
59350a9
21c37b1
45a1f2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,35 +167,7 @@ library Math { | |
// This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`. | ||
// Using an algorithm similar to the msb computation, we are able to compute `result = 2**(k/2)` which is a | ||
// good first approximation of `sqrt(a)` with at least 1 correct bit. | ||
uint256 result = 1; | ||
uint256 x = a; | ||
if (x >> 128 > 0) { | ||
x >>= 128; | ||
result <<= 64; | ||
} | ||
if (x >> 64 > 0) { | ||
x >>= 64; | ||
result <<= 32; | ||
} | ||
if (x >> 32 > 0) { | ||
x >>= 32; | ||
result <<= 16; | ||
} | ||
if (x >> 16 > 0) { | ||
x >>= 16; | ||
result <<= 8; | ||
} | ||
if (x >> 8 > 0) { | ||
x >>= 8; | ||
result <<= 4; | ||
} | ||
if (x >> 4 > 0) { | ||
x >>= 4; | ||
result <<= 2; | ||
} | ||
if (x >> 2 > 0) { | ||
result <<= 1; | ||
} | ||
uint256 result = 1 << (log2(a) / 2); | ||
|
||
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, | ||
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at | ||
|
@@ -223,4 +195,146 @@ library Math { | |
} | ||
return result; | ||
} | ||
|
||
/** | ||
* @dev Return the log in base 2, rounded down, of a positive value. | ||
* Returns 0 if given 0. | ||
*/ | ||
function log2(uint256 value) internal pure returns (uint256) { | ||
uint256 result = 0; | ||
if (value >> 128 > 0) { | ||
value >>= 128; | ||
result += 128; | ||
} | ||
if (value >> 64 > 0) { | ||
value >>= 64; | ||
result += 64; | ||
} | ||
if (value >> 32 > 0) { | ||
value >>= 32; | ||
result += 32; | ||
} | ||
if (value >> 16 > 0) { | ||
value >>= 16; | ||
result += 16; | ||
} | ||
if (value >> 8 > 0) { | ||
value >>= 8; | ||
result += 8; | ||
} | ||
if (value >> 4 > 0) { | ||
value >>= 4; | ||
result += 4; | ||
} | ||
if (value >> 2 > 0) { | ||
value >>= 2; | ||
result += 2; | ||
} | ||
if (value >> 1 > 0) { | ||
result += 1; | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* @dev Return the log in base 2, following the selected rounding direction, of a positive value. | ||
* Returns 0 if given 0. | ||
*/ | ||
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { | ||
uint256 result = log2(value); | ||
if (rounding == Rounding.Up && 1 << result < value) { | ||
result += 1; | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* @dev Return the log in base 10, rounded down, of a positive value. | ||
* Returns 0 if given 0. | ||
*/ | ||
function log10(uint256 value) internal pure returns (uint256) { | ||
uint256 result = 0; | ||
if (value >= 10**64) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Amxx what about this one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested both, and this change makes it more expensive :/ it looks like the compiler does the |
||
value /= 10**64; | ||
result += 64; | ||
} | ||
if (value >= 10**32) { | ||
value /= 10**32; | ||
result += 32; | ||
} | ||
if (value >= 10**16) { | ||
value /= 10**16; | ||
result += 16; | ||
} | ||
if (value >= 10**8) { | ||
value /= 10**8; | ||
result += 8; | ||
} | ||
if (value >= 10**4) { | ||
value /= 10**4; | ||
result += 4; | ||
} | ||
if (value >= 10**2) { | ||
value /= 10**2; | ||
result += 2; | ||
} | ||
if (value >= 10**1) { | ||
result += 1; | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* @dev Return the log in base 10, following the selected rounding direction, of a positive value. | ||
* Returns 0 if given 0. | ||
*/ | ||
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { | ||
uint256 result = log10(value); | ||
if (rounding == Rounding.Up && 10**result < value) { | ||
result += 1; | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* @dev Return the log in base 256, rounded down, of a positive value. | ||
* Returns 0 if given 0. | ||
* | ||
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. | ||
*/ | ||
function log256(uint256 value) internal pure returns (uint256) { | ||
uint256 result = 0; | ||
if (value >> 128 > 0) { | ||
value >>= 128; | ||
result += 16; | ||
} | ||
if (value >> 64 > 0) { | ||
value >>= 64; | ||
result += 8; | ||
} | ||
if (value >> 32 > 0) { | ||
value >>= 32; | ||
result += 4; | ||
} | ||
if (value >> 16 > 0) { | ||
value >>= 16; | ||
result += 2; | ||
} | ||
if (value >> 8 > 0) { | ||
result += 1; | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* @dev Return the log in base 10, following the selected rounding direction, of a positive value. | ||
* Returns 0 if given 0. | ||
*/ | ||
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { | ||
uint256 result = log256(value); | ||
if (rounding == Rounding.Up && 1 << (result * 8) < value) { | ||
result += 1; | ||
} | ||
return result; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can replace
1 << (log2(a) / 2)
by1 << (log2(a) >> 2)
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is interresting, but the correct formula would be
1 << (log2(a) >> 1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh, sorry. It >>1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before changing that I would check if Solidity does not make that optimization on its own.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tested both. using
>> 1
saves 54 gas compared to/ 2
(0.8.16 with optimizations)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Amxx Because It happen due to solidity checking underflow that. But When we are use shift operator it bypass that check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overflow check make sens for +, - and * ... not for /
The only reasonable check is that we don't divide by 0 ... but there is no reason to check that here.