在坚实的情况下,我们不能直接计算0.1或0.001。我们通常使用十进制乘数和除数整数来计算如下数字:
0.01 = 1 / 100当数字大于1时,比较容易,因为我们只是重复乘法:
function(uint base, uint exponent) external {
for (uint256 i = 1; i < exponent; i++)
uint256 z = base;
z = z * base;
}或者只是:
z ** exponent然而,当我们想使用分数阶数作为指数时,这些函数就不能工作了。有人能想出一个好办法吗?
发布于 2021-04-02 16:20:24
我目前只知道一种解决方案,由米哈伊尔符拉迪米罗夫编写。它以二进制分数的形式计算输入。
ABDKMath64x64.sol有一个exp_2函数,如下所示:
function exp_2(int128 x) internal pure returns (int128) {
unchecked {
require(x < 0x400000000000000000); // Overflow
if (x < -0x400000000000000000) return 0; // Underflow
uint256 result = 0x80000000000000000000000000000000;
// REMOVED: Binary fraction logic, explained below...
result >>= uint256(int256(63 - (x >> 64)));
require(result <= uint256(int256(MAX_64x64)));
return int128(int256(result));
}
}为了简洁起见,我跳过了函数体的一大部分。
任何小数都可以写成整数部分和分数部分的之和:
x = n + f, 0 <= f < 1然后,使用x作为指数:
y = 2^x = 2^(n + f) = 2^n * 2^f;注意到我们可以将f写成一个二元分数:
f = f1 * 2^-1 + f2 * 2^-2 + ...其中f_i可以是0或1。然后:
y = 2^n * (2^(f1*2^-1) * 2^(f2*2^-2) ...)请注意:
for f_i = 0 => 2^(f_i * 2^-i) = 1
for f_i = 1 => 2^(f_i * 2^-i) = root(2, 2^i)因此,我们预先计算了这些root(2, 2^-i)魔术因子,并使用它们作为硬编码常量。i表示位在小数二进制表示中的位置。如果我们要匹配第一位,我们将乘以2的平方根,即~1.4142。
根据基本对数规则:
x = b^(log_b(x))我们可以推断:
x^y = b^(y*log_b(x))适用于基地2:
x^y = 2^(y*log_2(x))我们已经为exp_2提供了一个实现,日志_2也由ABDKMath64x64提供。
我不喜欢米哈伊尔的解决方案:
发布于 2021-07-01 05:53:30
保罗的回答很好。如果你想逼近指数函数,我用了一系列的线性函数,目标半衰期:
function getPrice(uint256 value, uint256 t, uint256 halfLife) public pure returns (uint256 price) {
price = value >> (t / halfLife);
price -= price * (t % halfLife) / halfLife / 2;
}在我的代码中,半衰期是两天内的秒数。
https://ethereum.stackexchange.com/questions/79903
复制相似问题