Any Faster RMS Value Calculation in C?

后端 未结 5 1770
挽巷
挽巷 2020-12-16 03:29

I am writing a software for a small 8-bit microcontroller in C. Part of the code is to read the ADC value of a current transformer (ZCT), and then calculate the RMS value. T

5条回答
  •  刺人心
    刺人心 (楼主)
    2020-12-16 04:16

    1. divisions/multiplications by power of 2

      can be done by changing the exponent only via bit mask operations and +,- so mask/extract the exponent to integer value then apply bias. After that add/sub the value log2(operand) and encode back to your double value

    2. sqrt

      how fast and accurate it should be? You can use binary search on fixed point or use sqrt(x)=pow(x,0.5)=exp2(0.5*log2(x)). Again on fixed point it is quite easy to implement. You can temporarily make double a fixed point by taking the mantissa and bit shift it to some known exponent around your used values + handle the offset or to 2^0 if you have enough bits ...

      compute sqrt and then store back to double. If you do not need too big precision then you can stay on operand exponent and do the binary search directly on mantissa only.

    [edit1] binary search in C++

    //---------------------------------------------------------------------------
    double f64_sqrt(double x)
        {
        const int h=1;      // may be platform dependent MSB/LSB order
        const int l=0;
        DWORD b;            // bit mask
        int e;              // exponent
        union               // semi result
            {
            double f;       // 64bit floating point
            DWORD u[2];     // 2x32 bit uint
            } y;
        // fabs
        y.f=x;
        y.u[h]&=0x7FFFFFFF; x=y.f;
        // set safe exponent (~ abs half)
        e=((y.u[h]>>20)&0x07FF)-1023;
        e/=2;               // here can use bit shift with copying MSB ...
        y.u[h]=((e+1023)&0x07FF)<<20;
        // mantisa=0
        y.u[l] =0x00000000;
        y.u[h]&=0xFFF00000;
        // correct exponent
        if (y.f*y.f>x) { e--; y.u[h]=((e+1023)&0x07FF)<<20; }
        // binary search
        for (b =0x00080000;b;b>>=1) { y.u[h]|=b; if (y.f*y.f>x) y.u[h]^=b; }
        for (b =0x80000000;b;b>>=1) { y.u[l]|=b; if (y.f*y.f>x) y.u[l]^=b; }
        return y.f;
        }
    //---------------------------------------------------------------------------
    

    it returns sqrt(abs(x)) the results match "math.h" implementation from mine C++ IDE (BDS2006 Turbo C++). Exponent starts at half of the x value and is corrected by 1 for values x>1.0 if needed. The rest is pretty obvious

    Code is in C++ but it is still not optimized it can be surely improved ... If your platform does not know DWORD use unsigned int instead. If your platform does not support 32 bit integer types then chunk it to 4 x 16 bit values or 8 x 8 bit values. If you have 64 bit then use single value to speed up the process

    Do not forget to handle exponent also as 11 bit .... so for 8 bit registers only use 2 ... The FPU operations can be avoided if you multiply and compare just mantissas as integers. Also the multiplication itself is cumulative so you can use previous sub-result.

    [notes]

    For the bit positions see wiki double precision floating point format

提交回复
热议问题