8

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

ZMitton
  • 2,750
  • 4
  • 19
  • 34
  • @MaiaVictor Im adding a bounty for anyone who can come up with something here.

    Note: The Bancor equations most likely need this as well for their connector-weight exponent

    – ZMitton Jun 06 '18 at 15:32
  • 2
    Well you can read how bancor done this in their smart contract - https://github.com/bancorprotocol/contracts/blob/master/solidity/contracts/converter/BancorFormula.sol#L289 – leonprou Jun 11 '18 at 13:14
  • this is good. I've asked the author to explain how it works – ZMitton Jun 11 '18 at 17:14
  • You can see how it's done in C also - https://stackoverflow.com/questions/24174239/how-does-the-pow-function-work. It looks somewhat alike. – leonprou Jun 12 '18 at 07:38
  • @ZMitton damn I wasn't notified. A little bit late. Glad you found a solution! – MaiaVictor Jun 12 '22 at 10:12

2 Answers2

5

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.

Muhammad Altabba
  • 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
  • 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 https://github.com/Muhammad-Altabba/solidity-toolbox/blob/master/contracts/FractionalExponents.sol . – Muhammad Altabba Jun 20 '18 at 18:23
1

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.

Mikhail Vladimirov
  • 7,313
  • 1
  • 23
  • 38