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
2 Answers
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.
- 2,157
- 1
- 15
- 31
-
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 https://github.com/Muhammad-Altabba/solidity-toolbox/blob/master/contracts/FractionalExponents.sol . – 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.
- 7,313
- 1
- 23
- 38
Note: The Bancor equations most likely need this as well for their
– ZMitton Jun 06 '18 at 15:32connector-weightexponent