# Exponential function with fractional numbers

With solidity, we cannot directly calculate 0.1 or 0.001. We usually use a decimal multiplier and divisor integer to calculate numbers like this:

``````0.01 = 1 / 100
``````

When the number is more than 1, it is relatively easy because we just repeat the multiplication:

``````function(uint base, uint exponent) external {
for (uint256 i = 1; i < exponent; i++)
uint256 z = base;
z = z * base;
}
``````

Or just:

``````z ** exponent
``````

However, when we want to use fractional numbers as exponents, those functions won't work. Can anybody think of a nice way to do this?

• Maintain the value as a pair of integers (numerator and denominator) right up until the point where you have to return it. Alternatively, return it as a pair of integers and let the receiver handle that. Feb 16 '20 at 17:03
• Thanks, but seems like this easily causes integer overflow because it exceeds 2^256 when calculating numbers like 10^100 Feb 17 '20 at 1:35
• @kohshiba There are a couple of libraries that implement some floting point operations: github.com/cag/solidity-float-point-calculation, github.com/abdk-consulting/abdk-libraries-solidity. Unless you have a good reason I'd try to re-write the equation to use integers.
– Ismael
Feb 17 '20 at 17:10

# Binary Fractions

I currently know of only one solution, authored by Mikhail Vladimirov. It works by computing the input as a binary fraction.

However, I am still interested in other potential solutions, which is why I awarded a bounty.

## Code

ABDKMath64x64.sol has an exp_2 function, which looks like this:

``````function exp_2(int128 x) internal pure returns (int128) {
unchecked {
require(x < 0x400000000000000000); // Overflow

if (x < -0x400000000000000000) return 0; // Underflow

uint256 result = 0x80000000000000000000000000000000;

// REMOVED: Binary fraction logic, explained below...

result >>= uint256(int256(63 - (x >> 64)));
require(result <= uint256(int256(MAX_64x64)));

return int128(int256(result));
}
}
``````

I skipped a big chunk of the function body, for brevity.

## Explanation

Any fractional number can be written as the sum of the integer part and the fractional part:

``````x = n + f, 0 <= f < 1
``````

Then, using `x` as an exponent:

``````y = 2^x = 2^(n + f) = 2^n * 2^f;
``````

Observing that we can write `f` as a binary fraction:

``````f = f1 * 2^-1 + f2 * 2^-2 + ...
``````

Where f_i can be either 0 or 1. Then:

``````y = 2^n * (2^(f1*2^-1) * 2^(f2*2^-2) ...)
``````

Note that:

``````for f_i = 0 => 2^(f_i * 2^-i) = 1
for f_i = 1 => 2^(f_i * 2^-i) = root(2, 2^i)
``````

So we pre-compute these `root(2, 2^-i)` magic factors and use them as hardcoded constants. The `i` stands for the position of the bit in the fractional binary representation. E.g. if we were to match the very first bit, we would multiply by the square root of 2, i.e. ~1.4142.

## Arbitrary Base

Given the basic logarithm rule:

``````x = b^(log_b(x))
``````

We can deduce:

``````x^y = b^(y*log_b(x))
``````

Applying to base 2:

``````x^y = 2^(y*log_2(x))
``````

We already have an implementation for `exp_2` and log_2 is also offered by ABDKMath64x64.

What I don't like about Mikhail's solution:

1. Requires users to work with binary numbers
2. Precision is limited to int128, doesn't go to int256 or near that
• An implementation for this technique can be found in my advanced fixed-point math library PRBMath. Apr 20 at 14:52

Paul's answer is very good. If you want to approximate an exponential function, I used a series of linear functions with a target half-life:

``````function getPrice(uint256 value, uint256 t, uint256 halfLife) public pure returns (uint256 price) {
price = value >> (t / halfLife);
price -= price * (t % halfLife) / halfLife / 2;
}
``````

Half-life in my code was the number of seconds in two days.