首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Uniswapv3‘S对数库(TickMath.sol)是如何工作的?

Uniswapv3‘S对数库(TickMath.sol)是如何工作的?
EN

Ethereum用户
提问于 2021-11-18 14:05:47
回答 1查看 1.2K关注 0票数 5

我想用低汽油成本计算非标准基数的对数。第一种思路是在log2()这个线程中提到的不动点方法中使用基本规则的改变。

但是,Uniswap v3核心有自定义代码用于日志到基本√1.0001。它具有程序集级别的优化,我正在试图理解这些优化。然而,实现是不透明的,没有很好的记录。

根据我的理解:

  1. First :第一组汇编代码可能会这样做。为什么需要r
  2. 使用MSB获取log2:在找到log2之后,第二组程序集会做什么?
  3. 基本规则的变化:255738958999603826347141log_2 * 255738958999603826347141中的意义是什么?
  4. 减去3402992956809132418596140100660247210对于tickLow,加上291339464771989622907027621153398088495tickHi有什么意义?

TickMath.sol

代码语言:javascript
复制
    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
    /// ever return.
    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
        // second inequality must be < because the price can never reach the price at the max tick
        require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R');
        uint256 ratio = uint256(sqrtPriceX96) << 32;

        uint256 r = ratio;
        uint256 msb = 0;

        assembly {
            let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(5, gt(r, 0xFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(4, gt(r, 0xFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(3, gt(r, 0xFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(2, gt(r, 0xF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(1, gt(r, 0x3))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := gt(r, 0x1)
            msb := or(msb, f)
        }

        if (msb >= 128) r = ratio >> (msb - 127);
        else r = ratio << (127 - msb);

        int256 log_2 = (int256(msb) - 128) << 64;

        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(63, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(62, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(61, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(60, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(59, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(58, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(57, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(56, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(55, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(54, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(53, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(52, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(51, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(50, f))
        }

        int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number

        int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
        int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

        tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
    }
EN

回答 1

Ethereum用户

回答已采纳

发布于 2021-11-19 16:29:58

  1. First :第一组汇编代码可能会这样做。为什么需要r?

您是正确的,程序集的这一部分正在寻找作为log 2变量的初始整数近似的MSB。为了保留r变量,需要将ratio变量作为临时变量使用,如果您只关心第一个程序集块,则可以直接使用ratio并获得一个完全有效的MSB。

  1. 使用MSB获取log2:在找到log2之后,第二组程序集会做什么?

使用number的MSB,您可以得到log_2(number)作为整数的近似值。例如:

log_2(127) = 6.98868468677:MSB是6

log_2(128) = 7:MSB是7

log_2(129) = 7.01122725542:MSB是7

基本上,对于MSB =x,您知道log_2(value)< MSB + 1>= MSB,但您不知道它在这些值之间的确切位置。

在处理财务数据时,您可以理解为什么计算小数部分是绝对强制性的。这就是第二个组装块所做的工作,它改进了整数log_2近似,使小数部分的精度达到14位。

  1. 基本规则的变化: 255738958999603826347141在log_2 *255738958589603826347141中的意义是什么?

这些神奇的数字永远只是一个数学表达式,最终成为常数。在本例中,这是一个从2sqrt(1.0001)的基的更改。

log_(sqrt(1.0001)(number) = log_2(number) / log_2(sqrt(1.0001))

您可以将log_2(sqrt(1.0001))重写为:log_(sqrt(1.0001)(sqrt(1.0001) / log_(sqrt(1.0001)(2),翻译为:1 / log_(sqrt(1.0001)(2)

因此:

log_(sqrt(1.0001)(number) = log_2(number) * log_(sqrt(1.0001)(2)

现在log_(sqrt(1.0001)(2))是一个常数:13863.63.

前面的一行将MSB转换为Q64不动点号。

代码语言:javascript
复制
int256 log_2 = (int256(msb) - 128) << 64;

我们现在希望近似为Q128不动点数,如注释所示。因此,我们需要左移64或乘2 ** 64,这些是等价的。

结果表明,log_(sqrt(1.0001))(2) * 2 ** 64等于:2557389589996026347141(具有正确的精度)。

(我很难得出确切的值,但我认为这是因为精度/舍入错误,如果你做相反的路径,它是好的)

因此:

代码语言:javascript
复制
int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number

相当于:

代码语言:javascript
复制
// Not valid code, just for the example

// With power notation
int256 log_sqrt10001 = log_2 * log(sqrt(1.0001), 2) * 2 ** 64;

// With bit shift notation
 int256 log_sqrt10001 = log_2 * log(sqrt(1.0001), 2) << 64; 

其中,log(sqrt(1.0001), 2)更改基,2 ** 64将格式更改为Q128。

  1. 在寻找tickLow时减去340299292956809132418596140100660247210有什么意义?

我必须承认,我不清楚代码中的那个部分,所以我无法解释它。如果我发现了,我会编辑答案的!

票数 4
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/113844

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档