Efficient implementation of natural logarithm (ln) and exponentiation

后端 未结 8 2388
借酒劲吻你
借酒劲吻你 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:50

    Building off @Crouching Kitten's great natural log answer above, if you need it to be accurate for inputs <1 you can add a simple scaling factor. Below is an example in C++ that i've used in microcontrollers. It has a scaling factor of 256 and it's accurate to inputs down to 1/256 = ~0.04, and up to 2^32/256 = 16777215 (due to overflow of a uint32 variable).

    It's interesting to note that even on an STMF103 Arm M3 with no FPU, the float implementation below is significantly faster (eg 3x or better) than the 16 bit fixed-point implementation in libfixmath (that being said, this float implementation still takes a few thousand cycles so it's still not ~fast~)

    #include 
    
    float TempSensor::Ln(float y) 
    {
        // Algo from: https://stackoverflow.com/a/18454010
        // Accurate between (1 / scaling factor) < y < (2^32  / scaling factor). Read comments below for more info on how to extend this range
    
        float divisor, x, result;
        const float LN_2 = 0.69314718; //pre calculated constant used in calculations
        uint32_t log2 = 0;
    
    
        //handle if input is less than zero
        if (y <= 0)
        {
            return -FLT_MAX;
        }
    
        //scaling factor. The polynomial below is accurate when the input y>1, therefore using a scaling factor of 256 (aka 2^8) extends this to 1/256 or ~0.04. Given use of uint32_t, the input y must stay below 2^24 or 16777216 (aka 2^(32-8)), otherwise uint_y used below will overflow. Increasing the scaing factor will reduce the lower accuracy bound and also reduce the upper overflow bound. If you need the range to be wider, consider changing uint_y to a uint64_t
        const uint32_t SCALING_FACTOR = 256;
        const float LN_SCALING_FACTOR = 5.545177444; //this is the natural log of the scaling factor and needs to be precalculated
    
        y = y * SCALING_FACTOR; 
        
        uint32_t uint_y = (uint32_t)y;
        while (uint_y >>= 1) // Convert the number to an integer and then find the location of the MSB. This is the integer portion of Log2(y). See: https://stackoverflow.com/a/4970859/6630230
        {
            log2++;
        }
    
        divisor = (float)(1 << log2);
        x = y / divisor;    // FInd the remainder value between [1.0, 2.0] then calculate the natural log of this remainder using a polynomial approximation
        result = -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x; //This polynomial approximates ln(x) between [1,2]
    
        result = result + ((float)log2) * LN_2 - LN_SCALING_FACTOR; // Using the log product rule Log(A) + Log(B) = Log(AB) and the log base change rule log_x(A) = log_y(A)/Log_y(x), calculate all the components in base e and then sum them: = Ln(x_remainder) + (log_2(x_integer) * ln(2)) - ln(SCALING_FACTOR)
    
        return result;
    }
    

提交回复
热议问题