针对Cortex-M3的浮点点运算代替
2026/4/28 22:28:36 网站建设 项目流程

针对Cortex-M3的浮点点运算代替

浮点运算是完全由编译器用整数指令模拟的,开销巨大(一次单精度乘法约100周期,除法更慢)。利用M3优秀的 32 位整数运算能力,从根本上避免“用浮点思路”写算法。

1. 定点数(Q格式)-完全替代浮点的终极方案
定点数是M3上高性能数学运算的基石。原理就是:用整数表示小数,通过人为定标(比如 Q15:用 2^15 作为小数点位置)来保持精度。
常用定标:

  • Q31:精度最高,适合滤波累加(1.0 对应 0x7FFFFFFF)。
  • Q15:范围 [-1,1),适合音频、传感器归一化。
  • Q16.16:整数16位 + 小数16位,适合通用计算(1.5 = 0x18000)。
    示例:低通滤波器(对比浮点与定点)
// ===== 浮点原始版本(M3上极慢) =====floaty_float=0.0f;// 滤波器状态constfloatk=0.02f;// 滤波系数floatLowPass_Float(floatx){y_float+=k*(x-y_float);// 每次执行:1减法、1乘法、1加法(全是软件浮点)returny_float;}// ===== 定点优化版本(Q16.16格式) =====#defineK_FIXED(int32_t)(0.02f*65536)// 系数转为Q16.16,即 1310#defineONE_Q16(65536)// 1.0 在Q16.16的表示int32_ty_fixed=0;// Q16.16表示状态int32_tLowPass_Fixed(int32_tx_q16){// x也是Q16.16// error = x - y (Q16.16 直接相减)int32_terror=x_q16-y_fixed;// 乘法结果需要右移16位校正(因为两Q16.16相乘,积是Q32.32,我们只要Q16.16)int64_tprod=(int64_t)error*K_FIXED;// 使用64位临时变量防溢出y_fixed+=(int32_t)(prod>>16);// 校正回Q16.16returny_fixed;// 返回值也是Q16.16}// 使用示例:ADC 12位结果转为Q16.16uint16_tadc_val=ADC_Read();int32_tx=((int32_t)adc_val<<16)/4096;// 归一化到0~1范围的Q16.16int32_tfiltered=LowPass_Fixed(x);// 转回实际电压:实际值 = filtered * 3.3 / 65536 (谨慎使用,可保持内部Q格式)

效果对比:

  • 浮点版一次调用 ~300 CPU 周期(M3库函数)。
  • 定点版一次调用 <10 CPU 周期(仅 1 次减法、1 次64位乘法、1 次移位加法)。

2.除法预倒数+乘法代替(不可避免的浮点)
如果迫不得已要使用浮点(例如与其他模块接口匹配),务必将除法转换为乘法。

// 场景:对一组ADC样本乘以缩放因子 scale = adc_val / 4096.0f// × 低效写法:循环内直接除法voidScaleArray_Float(float*buf,intn){for(inti=0;i<n;i++){buf[i]=buf[i]/4096.0f;// 每次循环都做浮点除法}}// √ 优化:预计算倒数,循环内变乘法voidScaleArray_Float_Opt(float*buf,intn){floatinv=1.0f/4096.0f;// 只算一次除法for(inti=0;i<n;i++){buf[i]*=inv;// 乘法比除法快数倍}}

如果 4096.0f 是编译时常量,更推荐使用宏或常量定义其倒数,消灭所有除法。

3.复杂数学函数的查表+线性差值
M3上严禁直接调用标准库的 sinf、expf 等函数(内部基于浮点多项式,极慢)。用查表法替换。
举例:UART控制舵机,需要计算正弦值生成PWM

// 预先在程序中存储一个周期的正弦表(Q15格式)#defineSIN_TABLE_SIZE256constint16_tsin_table[SIN_TABLE_SIZE]={0,804,1608,2410,3212,4011,4808,5602,// i=0..76393,7179,7962,8739,9512,10278,11039,11793,// i=8..1512539,13279,14010,14732,15446,16151,16846,17530,// i=16..2318204,18868,19519,20159,20787,21403,22005,22594,// i=24..3123170,23731,24279,24811,25329,25832,26319,26790,// i=32..3927245,27683,28105,28510,28898,29268,29621,29956,// i=40..4730273,30571,30852,31113,31356,31580,31785,31970,// i=48..5532137,32285,32412,32521,32609,32678,32728,32757,// i=56..6332767,32757,32728,32678,32609,32521,32412,32285,// i=64..71 (90°附近对称)32137,31970,31785,31580,31356,31113,30852,30571,// i=72..7930273,29956,29621,29268,28898,28510,28105,27683,// i=80..8727245,26790,26319,25832,25329,24811,24279,23731,// i=88..9523170,22594,22005,21403,20787,20159,19519,18868,// i=96..10318204,17530,16846,16151,15446,14732,14010,13279,// i=104..11112539,11793,11039,10278,9512,8739,7962,7179,// i=112..1196393,5602,4808,4011,3212,2410,1608,804,// i=120..1270,-804,-1608,-2410,-3212,-4011,-4808,-5602,// i=128..135 (下半周期,负值)-6393,-7179,-7962,-8739,-9512,-10278,-11039,-11793,// i=136..143-12539,-13279,-14010,-14732,-15446,-16151,-16846,-17530,// i=144..151-18204,-18868,-19519,-20159,-20787,-21403,-22005,-22594,// i=152..159-23170,-23731,-24279,-24811,-25329,-25832,-26319,-26790,// i=160..167-27245,-27683,-28105,-28510,-28898,-29268,-29621,-29956,// i=168..175-30273,-30571,-30852,-31113,-31356,-31580,-31785,-31970,// i=176..183-32137,-32285,-32412,-32521,-32609,-32678,-32728,-32757,// i=184..191-32767,-32757,-32728,-32678,-32609,-32521,-32412,-32285,// i=192..199 (270°附近,最小值-32767)-32137,-31970,-31785,-31580,-31356,-31113,-30852,-30571,// i=200..207-30273,-29956,-29621,-29268,-28898,-28510,-28105,-27683,// i=208..215-27245,-26790,-26319,-25832,-25329,-24811,-24279,-23731,// i=216..223-23170,-22594,-22005,-21403,-20787,-20159,-19519,-18868,// i=224..231-18204,-17530,-16846,-16151,-15446,-14732,-14010,-13279,// i=232..239-12539,-11793,-11039,-10278,-9512,-8739,-7962,-7179,// i=240..247-6393,-5602,-4808,-4011,-3212,-2410,-1608,-804// i=248..255//索引 64 为 32767(对应 90° 正弦峰值),索引 192 为 -32767(对应 270°)。Q15 的最大正值取 32767 而非 32768,是为了防止不对称溢出,这也是标准做法。};int16_tFast_Sin_Q15(uint16_tangle){// angle 0~65535 代表 0~360度uint16_tindex=angle>>8;// 取高8位索引0~255returnsin_table[index];// 纯取表,<5周期}

更精确的线性插值版(保持光滑度)

int16_tFast_Sin_Interp(uint16_tangle){uint16_tidx=angle>>8;// 整数索引0~255uint8_tfrac=(angle>>4)&0x0F;// 小数部分,4位插值分辨率int16_ta=sin_table[idx];int16_tb=sin_table[(idx+1)&0xFF];// 下一个点(循环)// 线性插值: a + (b-a)*frac/16returna+(((int32_t)(b-a)*frac)>>4);}

性能:几行整数运算代替上百周期的 sinf,精度足够舵机应用。

总结:M3(STM32F1)上避免浮点的完整策略

解决方法适用场景性能提升(相对软件浮点)
定点Q15/Q31/Q16.16滤波、PID、传感器融合、音频处理50~200倍(避免浮点库调用)
除法变乘法循环内有除法、缩放因子固定3~15倍(除法开销远大于乘法)
查表+插值sin、cos、sqrt、log等复杂函数10~50倍(省掉多项式迭代)
编译器选项任何浮点操作额外 10~30% 提升

绝不把浮点放在控制环路或高频中断里。从需求分析阶段就选择整数mV、千分之一度等定点单位,在设计源头消灭浮点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询