在嵌入式C语言中处理小数



我有我的代码下面,我想问什么是最好的方法来解决数字(除法,乘法,对数,指数)高达4位小数点?我使用PIC16F1789作为我的设备。

float sensorValue;
float sensorAverage;
void main(){
    //Get an average data by testing 100 times
    for(int x = 0; x < 100; x++){
        // Get the total sum of all 100 data
        sensorValue = (sensorValue + ADC_GetConversion(SENSOR));
    }
    // Get the average
    sensorAverage = sensorValue/100.0;
}

一般来说,在mcu上,浮点类型的处理成本(时钟、代码)比整型高。虽然对于有硬件浮点单元的设备来说,这通常是正确的,但对于没有浮点单元的设备(如PIC16/18控制器),它成为至关重要的信息。它们必须在软件中模拟所有浮点运算。这很容易使每次加法花费超过100个时钟周期(乘法要多得多),并使代码膨胀。

因此,最好避免使用float(更不用说在这样的系统上使用double了)。

对于您的示例,ADC返回一个整数类型,因此求和可以完全使用整数类型完成。你只需要确保总和不会溢出,所以它必须为你的代码保留~100 *。

最后,要计算平均值,您可以将整数除以迭代次数(四舍五入到零),或者-更好-应用简单的"四舍五入":

#define NUMBER_OF_ITERATIONS 100
sensorAverage = (sensorValue + NUMBER_OF_ITERATIONS / 2) / NUMBER_OF_ITERATIONS;

如果你真的想加快你的代码,设置NUMBER_OF_ITERATIONS为2的幂(这里是64或128),如果你的代码可以容忍的话。

最后:为了得到除法的整数部分,您可以将和(sensoreValue)视为分数值。对于给定的100次迭代,您可以将其视为十进制分数:在转换为字符串时,只需在较低的2位数字左边打印小数点。当你除以100时,小数的有效数字不超过两位。如果你确实需要4位数字,例如用于其他操作,你可以的和乘以100(实际上,它是10000,但是你已经在循环中将它乘以100)。

这叫做小数点定点。更快的处理(用移位代替乘法)是使用二进制定点,正如我上面所说的。

在PIC16上,我强烈建议考虑使用二进制分数,因为在这个平台上乘法和除法非常昂贵。一般来说,它们不太适合信号处理。如果您需要维持一些性能,ARM Cortex-M0或M4将是更好的选择-价格相似。

在您的示例中,完全避免非整数表示是微不足道的,但是为了更普遍地回答您的问题,ISO兼容的编译器将支持浮点算术和库,但出于性能和代码大小的原因,您可能希望避免这种情况。

定点运算可能是你需要的。对于简单的计算,可以使用特定的固定点方法,例如,您将示例中的sensorAverage的单位视为百分之一(1/100),并完全避免昂贵的除法。但是,如果您想执行完整的数学库操作,那么更好的方法是使用定点库。Anthony Williams在《用不动点算法优化应用程序》中提出了一个这样的库。代码是c++, PIC16可能缺乏一个像样的c++编译器,但方法可以稍微不那么优雅地移植到C。它还使用了一个巨大的64位定点36Q28格式,这在PIC16上将是昂贵和缓慢的;你可能想把它改成16Q16。

如果你真的关心性能,坚持整数算术,尝试使样本的数量平均为2的幂,这样除法可以通过位移位的方式进行,但是如果它不是2的幂,比如100(正如Olaf指出的固定点),你也可以使用位移位和加法:我怎么能只使用位移位和加法乘法和除法?

如果你不关心性能,仍然想要使用浮点数(你已经得到警告,这在PIC16中可能不是很快,可能会使用大量的flash), math.h有以下函数:http://en.cppreference.com/w/c/numeric/math包括幂运算:pow(基数,exp)和对数*仅底数2,底数10和底数e,对于任意基数使用基数对数属性的变化

最新更新