Есть ли эффективный способ вычисления экспоненциальности дробного базиса и дробного показателя?
Во-первых, не такой вопрос, как this (что отлично). Мне нужно, чтобы показатель был дробным. Что-то вроде 2.5 ^ 0.75
1 ответ
Код хорошего решения:
function power(uint256 _baseN, uint256 _baseD, uint32 _expN, uint32 _expD) internal view returns (uint256, uint8) {
assert(_baseN < MAX_NUM);
uint256 baseLog;
uint256 base = _baseN * FIXED_1 / _baseD;
if (base < OPT_LOG_MAX_VAL) {
baseLog = optimalLog(base);
}
else {
baseLog = generalLog(base);
}
uint256 baseLogTimesExp = baseLog * _expN / _expD;
if (baseLogTimesExp < OPT_EXP_MAX_VAL) {
return (optimalExp(baseLogTimesExp), MAX_PRECISION);
}
else {
uint8 precision = findPositionInMaxExpArray(baseLogTimesExp);
return (generalExp(baseLogTimesExp >> (MAX_PRECISION - precision), precision), precision);
}
}
Полный рабочий код: https: //github.com/Muhammad-Altabba/solidity-toolbox/blob/master/contracts/FractionalExponents.sol
Некоторое объяснение
Число с плавающей запятой x
может быть представлено в двух числах: a/b
if x = a/b and y = c/d, then x ^ y = (a/b) ^ (c/d)
Проблема
Проблема в том, что если код написан как (a/b) ^ (c/d)
, точность результата будет беспорядочна. Потому что разделение a/b
и c/d
будет разбить плавающую точку.
В другой попытке выражение (a/b) ^ (c/d)
может быть оценено как a ^ (c/d) / b ^ (c/d)
или (a^c + a^(1/d)) / (b^c + b^(1/d))
.
Проблема заключается в том, что питание a
до c
или даже c/d
может легко переполнить uint256! Хотя конечное итоговое значение может быть небольшим uint8. (это объясняется здесь )
Решение
Существует приблизительное значение для x ^ y
, как показано ниже:
x ^ y = e ^ (log(x) * y)
Фактически, метод выше зависит от этого уравнения для вычисления энергетической функции. Как следует:
(_baseN / _baseD) ^ (_expN /_expD) = e ^ (log(base) * exp)
Где base = _baseN / _baseD
(обратите внимание, что FIXEX_1, который используется в коде, используется только для сдвига числа слева, чтобы обеспечить лучшая точность разделения.
И exp = _expN /_expD
.
Ключевым моментом здесь является вычисление log(base) * exp
перед включением питания e
, предотвратит переполнение по сравнению с ранней обсуждаемой формулой, может легко произвести переполнение: a ^ (c/d) / b ^ (c/d)
или (a^c + a^(1/d)) / (b^c + b^(1/d))
Однако для optimalLog
vs. generalLog
и optimalExp
vs. generalExp
, что используется в предоставленном коде, они предназначены для вычисления кода log
и e ^
либо в оптимальном, либо приближенном вычислении.
Спасибо leonprou за его ценные замечания по этому вопросу.