First off - not the same question as this (which is great). I need the exponent to be fractional as well. Something like 2.5^0.75
The code of a good solution:
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);
}
}
Full working code at: https://github.com/Muhammad-Altabba/solidity-toolbox/blob/master/contracts/FractionalExponents.sol
Some explanation
A floating point number x
can be represented in two numbers: a/b
if x = a/b and y = c/d, then x ^ y = (a/b) ^ (c/d)
The problem
The problem is that if the a code is written as (a/b) ^ (c/d)
, the accuracy of the result would be a mess. Because the division of a/b
and c/d
would smash the floating point.
Going into another try, the expression (a/b) ^ (c/d)
could be evaluated as a ^ (c/d) / b ^ (c/d)
or (a^c + a^(1/d)) / (b^c + b^(1/d))
.
The problem with this is that the powering a
to c
or even to c/d
could easily overflow uint256! Even though, the final resulting value could be a small uint8. (this is explained here)
The Solution
There is an approximation for x ^ y
as follow:
x ^ y = e ^ (log(x) * y)
Actually, the method above depend on this equation to compute the power function. As follow:
(_baseN / _baseD) ^ (_expN /_expD) = e ^ (log(base) * exp)
Where base = _baseN / _baseD
(note that FIXEX_1 that is used in the code, is just used to shift the number of left in order to provide better accuracy of the division.
And exp = _expN /_expD
.
The key point here is calculating the log(base) * exp
before powering to e
, will prevent overflow compared to the early discussed formula the can easily produce overflow: a ^ (c/d) / b ^ (c/d)
or (a^c + a^(1/d)) / (b^c + b^(1/d))
.
However, for optimalLog
vs. generalLog
and optimalExp
vs. generalExp
that is used in the provided code, they is about calculating the log
and e ^
either in an optimal or an approximate calculation.
Thanks leonprou for his valuable comments on the question.
-
I gave you the check mark but you've removed some of the functions. Could you please include those so that this compiles? – ZMitton Jun 20 '18 at 16:11
-
1I did not include the full code in the answer because it is quite long with 350+ lines of code. However, upon your request I created a file that contains only and all the needed functions at github.com/Muhammad-Altabba/solidity-toolbox/blob/master/… . – Muhammad Altabba Jun 20 '18 at 18:23
You may calculate x^y
as 2^(y log_2 x)
using ABDK Math 64.64 library, that has both: 2^x
and log_2 x
functions implemented for binary fixed point numbers. This way is especially efficient in case you need to calculate x^y
multiple times for the same x
but different y
, because you may calculate log_2 x
once and reuse.
connector-weight
exponent – ZMitton Jun 6 '18 at 15:32