1. 链一财经首页
  2. 资讯

关于solidity中数值的指数和对数教程-第五节

  简   介
对数函数与指数函数(对数函数是指数函数的倒数)一起,可以把乘法变成加法,更重要的是,可以把指数函数变成乘法。由于以下两个规则,这是可能的:

在对这些方程的左右部分求幂之后,我们得到:

请注意,这些公式适用于除一个以外的任意正数基数,因此我们可以选择便于实现的基数。
在本文中,我们将展示如何在Solidity中有效地实现以2为底的对数和指数函数,如何将这些以2为底的函数转换为相应的自然(以e为底)函数,以及在DeFi应用程序中这些函数的实际用例是什么?。
因此,本文的重点是指数和对数。
对   数
让我们从二进制(以2为底)对数开始。x的二进制对数是y,使得:

显然,要使y存在,x的值必须为正。
请注意,如果

然后

所以n是x的二进制对数的整数部分。因此我们的第一个问题是:
如何计算Solidity中二元对数的整数部分?
这是适用于正整数x的简单方法:
for (n = 0; x > 1; x >>= 1) n += 1;
尽管该方法简单明了,但其gas消耗量为O(n),因此非常昂贵。这是适用于256位正整数x的改进版本:
if (x >= 2**128) { x >>= 128; n += 128; }
if (x >= 2**64) { x >>= 64; n += 64; }
if (x >= 2**32) { x >>= 32; n += 32; }
if (x >= 2**16) { x >>= 16; n += 16; }
if (x >= 2**8) { x >>= 8; n += 8; }
if (x >= 2**4) { x >>= 4; n += 4; }
if (x >= 2**2) { x >>= 2; n += 2; }
if (x >= 2**1) { /* x >>= 1; */ n += 1; }
这种改进的实施方案在最坏的情况下消耗了约600gas:比原始的未经优化的gas减少25倍。
如果X是小数怎么办?
在Solidity语言的核心中没有分数,但是有几种方法可以模拟这些数字。二进制定点和二进制浮点。两种方式都表示分数x,如下所示:

其中m和e是整数。值m称为尾数,e称为指数。二进制定点数和浮点数的区别在于,定点数的指数是一个预定义的常数,通常为负数,因此只需存储尾数;而浮点数的指数是可变的,因此必须与尾数一起存储。
现在请注意,

因此可以将二进制定点或浮点数的二进制对数计算为尾数加上指数的二进制对数。只要指数是整数,相同的公式也适用于对数的整数部分。
现在,当我们知道如何为整数部分提供资金时,
二进制对数的小数部分呢?
假设n是x的二进制对数的整数部分,则对数的小数部分可以这样计算:

注意,只要

然后

因此可以将计算二进制对数的小数部分推导出来计算介于1(含)和2(不含)之间的数字的二进制对数。为了进行此计算,我们将使用以下两个规则:

以下是编写的代码,好像Solidity本身支持小数:
for (delta = 1; delta >= precision; delta /= 2) {
  if (x >= 2) { result += delta; x /= 2; }
  x *= x;
}
在每次迭代中,我们都应用前一个规则:将x的值平方,然后将delta的值减半。如果在某个点x的值变得大于或等于2,则我们应用后一个规则:将delta加到result并将x的值减半。我们重复循环直到增量降到所需的precision以下为止,因为进行计算不会对result产生任何重大影响。
不幸的是,Solidity本身不支持分数,因此实际代码如下所示:
for (delta = ONE;
     gte (delta, precision);
     delta = div (delta, TWO)) {
  if (gte (x, TWO)) {
    result = add (resukt, delta);
    x = div (x, TWO);
  }
  x = mul (x, x);
}
其中ONE,TWO,两个,add,mul,div和gte是常量和函数,它们模拟某种分数数字并对其进行算术运算。
幸运的是,ABDK库已经准备好对64.64位二进制定点四精度二进制浮点数使用二进制对数实现。
现在当我们知道如何计算二进制对数时,
那自然对数和公共对数呢?
为了计算自然对数(以e为底)和公共对数(以10为底),我们可以使用以下规则:

因此

得到

可以将其硬编码到实施中,而无需在运行时进行计算。
现在当我们完成对数后,让我们切换到
    指   数
同样让我们从以2为底的幂开始,即

Solidity具有**运算符,因此显而易见的解决方案是:
y = 2**x
但是这仅适用于x的整数和非负值。而且这并不是最有效的一种,因为使用移位操作会更便宜:
y = 1 << x
移位可能还有助于x的负值:
y = x >= 0 ? 1 << x : 1 >> -x
由于Solidity本身不支持分数,因此任何负x都将导致零结果,这没有太大意义。但是如果将定点表示形式1替换为整数1,则此代码将变得更加合理。
对于二进制浮点数甚至更简单,因为在上面的公式中y是尾数等于1且指数等于x的二进制浮点数。
如果x是小数怎么办?
让我们将x的一个小数值分成整数部分n和小数部分f:

然后

令f为二进制分数:

注意:

因数

可以预先计算,无需在运行时进行计算:

这很好,但是我们应该预先计算多少神奇的因数?
对于二进制定点数,答案是显而易见的,因为点后的二进制数是固定的。所以如果不动点的小数部分有64位,那么我们需要64个神奇的因数。
对于二进制浮点数,情况要复杂一些,因为尾数m可通过大的负指数e右移。因此此类浮点数的二进制表示形式如下所示:

幸运的是,对于介于0和1之间的任何f

因此,如果f是上面显示其二进制表示形式的数字,则

因此,如果期望的结果精度为M位,那么我们可以忽略f的二进制表示形式的那些位,这些位的位置比点后的M个二进制位置更远。这样我们最多需要预先计算的M个魔术因子来计算指数。
可以在ABDK库源代码中找到针对二进制定点和浮点数的基于2指数函数的即用型实现。
以2为底的指数是好的,但是那么任意基数的指数呢?
我们知道,对于除一个以外的任意正x和任意正b,以下条件成立:

因此,对于任意y:

对于b = 2,这给我们:

由于我们已经知道如何计算以2为底的对数和指数函数,因此我们可以使用任意底数来计算指数函数。
该公式可用于有效计算连续复利:

在此,r是单个时间单位的利率,t是要计算其复利的时间间隔的长度。请注意在固定利率的情况下,

只能计算一次,可能是在链外,然后再使用,这将使此公式更加有效。
结   论
在本文中,我们展示了如何在Solidity中有效地计算以2为底的对数和指数函数以二进制定点和浮点数。
我们还描述了如何通过base-2函数实现基于任意对数和指数的函数。
我们介绍了使用对数和指数函数有效执行的连续复利计算的实际用例。
在下一篇文章中,我们将显示DeFi应用程序中对数和指数的更多用例,下一个主题是:Bancor公式。

根据国家《关于防范代币发行融资风险的公告》,大家应警惕代币发行融资与交易的风险隐患。

本文来自LIANYI转载,不代表链一财经立场,转载请联系原作者。

发表评论

登录后才能评论