Zunächst einmal - nicht dieselbe Frage wie diese (was großartig ist). Ich brauche den Exponenten, um auch gebrochen zu sein. Etwa 2,5^0,75
Der Code einer guten Lösung:
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);
}
}
Vollständiger Arbeitscode unter: https://github.com/Muhammad-Altabba/solidity-toolbox/blob/master/contracts/FractionalExponents.sol
Einige Erklärung
Eine Fließkommazahl x
kann in zwei Zahlen dargestellt werden:a/b
if x = a/b and y = c/d, then x ^ y = (a/b) ^ (c/d)
Das Problem
Das Problem ist, dass, wenn der a-Code als geschrieben wird (a/b) ^ (c/d)
, die Genauigkeit des Ergebnisses ein Durcheinander wäre. Denn die Division von a/b
und c/d
würde die Fließkommazahl zerschlagen.
Bei einem weiteren Versuch (a/b) ^ (c/d)
könnte der Ausdruck als a ^ (c/d) / b ^ (c/d)
oder ausgewertet werden (a^c + a^(1/d)) / (b^c + b^(1/d))
. Das Problem dabei ist, dass das Einschalten a
von c
oder sogar von c/d
uint256 leicht überlaufen könnte! Der resultierende Endwert könnte jedoch ein kleiner uint8 sein. (das wird hier erklärt )
Die Lösung
Es gibt eine Annäherung für x ^ y
wie folgt:
x ^ y = e ^ (log(x) * y)
Tatsächlich hängt das obige Verfahren von dieser Gleichung ab, um die Potenzfunktion zu berechnen. Wie folgt:
(_baseN / _baseD) ^ (_expN /_expD) = e ^ (log(base) * exp)
Wo base = _baseN / _baseD
(beachten Sie, dass FIXEX_1, das im Code verwendet wird, nur verwendet wird, um die Zahl nach links zu verschieben, um eine bessere Genauigkeit der Division zu erreichen. Und exp = _expN /_expD
.
Der entscheidende Punkt hier ist die Berechnung der log(base) * exp
vor dem Einschalten von e
, verhindert einen Überlauf im Vergleich zu der früh diskutierten Formel, die leicht einen Überlauf erzeugen kann: a ^ (c/d) / b ^ (c/d)
oder (a^c + a^(1/d)) / (b^c + b^(1/d))
.
Bei optimalLog
vs. generalLog
und optimalExp
vs. generalExp
, die im bereitgestellten Code verwendet werden, geht es jedoch um die Berechnung von log
und e ^
entweder in einer optimalen oder einer ungefähren Berechnung.
Danke Leonprou für seine wertvollen Kommentare zu dieser Frage.
Sie können mit der ABDK Math 64.64- Bibliothek rechnen, die sowohl: x^y
als auch Funktionen für binäre Festkommazahlen implementiert hat. Diese Methode ist besonders effizient, wenn Sie mehrere Male für dasselbe , aber unterschiedliche berechnen müssen , da Sie einmal berechnen und wiederverwenden können.2^(y log_2 x)
2^x
log_2 x
x^y
x
y
log_2 x
ZMitton
connector-weight
Exponentenleonprou
ZMitton
leonprou
MaiaVictor