7

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

4

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
  • 1
    I 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
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.