-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathFixedPointMathLib.sol
66 lines (62 loc) · 3.3 KB
/
FixedPointMathLib.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
pragma solidity 0.8.10;
// based on https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol
library FixedPointMathLib {
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
// This segment is to get a reasonable initial estimate for the Babylonian method.
// If the initial estimate is bad, the number of correct bits increases ~linearly
// each iteration instead of ~quadratically.
// The idea is to get z*z*y within a small factor of x.
// More iterations here gets y in a tighter range. Currently, we will have
// y in [256, 256*2^16). We ensure y>= 256 so that the relative difference
// between y and y+1 is small. If x < 256 this is not possible, but those cases
// are easy enough to verify exhaustively.
z := 181 // The 'correct' value is 1, but this saves a multiply later
let y := x
// Note that we check y>= 2^(k + 8) but shift right by k bits each branch,
// this is to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8),
// and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of x, or about 20bps.
// The estimate sqrt(x) = (181/1024) * (x+1) is off by a factor of ~2.83 both when x=1
// and when x = 256 or 1/256. In the worst case, this needs seven Babylonian iterations.
z := shr(18, mul(z, add(y, 65536))) // A multiply is saved from the initial z := 181
// Run the Babylonian method seven times. This should be enough given initial estimate.
// Possibly with a quadratic/cubic polynomial above we could get 4-6.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// See https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division.
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This check ensures we return floor.
// The solmate implementation assigns zRoundDown := div(x, z) first, but
// since this case is rare, we choose to save gas on the assignment and
// repeat division in the rare case.
// If you don't care whether floor or ceil is returned, you can skip this.
if lt(div(x, z), z) {
z := div(x, z)
}
}
}
}