Efficient implementation of natural logarithm (ln) and exponentiation

后端 未结 8 2372
借酒劲吻你
借酒劲吻你 2020-12-15 09:18

I\'m looking for implementation of log() and exp() functions provided in C library . I\'m working with 8 bit microcontro

8条回答
  •  北海茫月
    2020-12-15 09:49

    Using Taylor series is not the simplest neither the fastest way of doing this. Most professional implementations are using approximating polynomials. I'll show you how to generate one in Maple (it is a computer algebra program), using the Remez algorithm.

    For 3 digits of accuracy execute the following commands in Maple:

    with(numapprox):
    Digits := 8
    minimax(ln(x), x = 1 .. 2, 4, 1, 'maxerror')
    maxerror
    

    Its response is the following polynomial:

    -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x
    

    With the maximal error of: 0.000061011436

    We generated a polynomial which approximates the ln(x), but only inside the [1..2] interval. Increasing the interval is not wise, because that would increase the maximal error even more. Instead of that, do the following decomposition:

    So first find the highest power of 2, which is still smaller than the number (See: What is the fastest/most efficient way to find the highest set bit (msb) in an integer in C?). That number is actually the base-2 logarithm. Divide with that value, then the result gets into the 1..2 interval. At the end we will have to add n*ln(2) to get the final result.

    An example implementation for numbers >= 1:

    float ln(float y) {
        int log2;
        float divisor, x, result;
    
        log2 = msb((int)y); // See: https://stackoverflow.com/a/4970859/6630230
        divisor = (float)(1 << log2);
        x = y / divisor;    // normalized value between [1.0, 2.0]
    
        result = -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x;
        result += ((float)log2) * 0.69314718; // ln(2) = 0.69314718
    
        return result;
    }
    

    Although if you plan to use it only in the [1.0, 2.0] interval, then the function is like:

    float ln(float x) {
        return -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x;
    }
    

提交回复
热议问题