From OSDev Wiki
Jump to: navigation, search

After spending some time studying mathematics, I've found of a fairly easy way to calculate logarithms in your operating system. Note that there are a few downsides to this way as well, which will be described in a moment.




Firstly, there are a few requirements you need to meet in order to be able to use this method. You will need:

  • Floating point support (see FPU for more details).
  • Some implementation of the powf function (a function that can raise float x to float y). If y is the power to raise to, then you must be able to handle the case where 0 < y < 1.

This is basically all you need.


The main downside of this method is precision: it is not very precise and the way I'm showing it to you may be improved (it is merely used for demonstration). The method will probably suffer from rounding errors.

The Method

For the calculation of logarithms, we're going to use a couple of other facts.

Firstly, the derivative of an exponential function f(x) = a^x (where ^ denotes to the power of) is f'(x) = c * a^x. c is a constant number that depends on the growth factor (the growth factor is a). The number c can be calculated by

 lim    a   - 1
 Δx->0 ---------

But the derivative of function f(x) = a^x also equals g'(x) = ln(a) * a^x, where ln is the logarithmus naturalis of a. What we have now are two derivatives of one same function, which means we can compare them.

 -> c * a^x = ln(a) * a^x             f'(x) = g(x)
 -> c = ln(a)                         Drop a^x (exists in both terms)
 -> c = log(a) = the_limit_above      ln(a) = log(a)

Note that log is the logarithmus naturalis in the standard C library (so the base number is Euler's mathematical constant, not 10).

From the definition of logarithms, we know that

 a         log10(x)
  log x = ----------

So when calculating the ln (or log in the standard C library) of x, a becomes e. Because log10(e) will never change, we can calculate this number and put it in a define somewhere. By my (quick and dirty) calculations, log10(e) equals approximately 0.4342944819. Now we can transform our formula:

 0.4342944819 *  log x = log10(x)

Now, if we want to implement our C function log (which uses e as base, so a = e), we use the fact that log(x) = c and the limit.

float log10(float x)
   return (((powf(x, 0.00001f) - 1) / 0.00001f) * LOG10_E);

Where LOG10_E is the number calculated earlier. We raise x to the power of 0.00001f because Δx approaches 0, so this is a fairly good choice (though inaccurate). Once you have log10, it's not hard to calculate the other logarithm functions:

float log(float x)
   return (log10(x) / LOG10_E);

We could even create a general function that calculates any type of logarithm:

// ----- Custom function that calculates the logarithm of value x with the given base.
loat logx(float x, float base)
   // base may not equal 1 or be negative.
   if(base == 1.f || base < 0.f)
      return 0.f; // Return what you want here, could be Not-A-Number or NAN.
   return (log10(x) / log10(base));

As I said, this method is inaccurate and probably could be improved to be more precise.

Alternative implementation (courtesy of Combuster)

There is an alternative way (but more complex in theory) that can be used to calculate the ln of a value x. It is much more precise (since it uses a Taylor series expansion). Wikipedia has a very nice article on this, which can be found here. Once you have implemented this way, it is again not very hard to calculate other logarithmic values.

Personal tools