Arduino Nano 上函数乘1000返回异常 乘1e6则结果正常
异常根因
你遇到的计算错误本质是16位整型运算截断,属于C语言整型运算规则导致的逻辑问题,和GCC优化等级无关,哪怕关闭所有优化(O0)也会复现。
具体逻辑链条:
- Arduino Nano搭载的ATmega328P属于AVR 8位架构,AVR-GCC对整型字面量的类型判定有固定规则:
1000这个数值完全落在16位有符号int(取值范围-32768 ~ 32767)的表示区间内,会被直接识别为16位的int类型常量 - 传入函数的参数
val是16位的unsigned short类型,它和16位int常量做乘法时,整个运算结果的类型就是16位int,运算过程只保留16位的计算结果 - 代入数值验证:200 * 1000的数学结果是200000,远超过16位整型的最大表示范围,做16位截断后,保留的低16位数值恰好是3392——哪怕函数返回值是32位的
unsigned long,乘法运算已经在16位精度下完成截断,自然返回错误结果。
至于为什么把乘数改成1000000时运算完全正常:
1000000的数值已经远超16位有符号int的表示上限,AVR-GCC会自动将其识别为32位的long类型常量- 当16位的
unsigned short和32位long常量做乘法时,整个运算会自动提升到32位精度完成,不会出现16位截断,因此能得到正确结果。
修复方法
只需要强制乘法过程使用32位精度计算即可,两种常用修改方式:
- 给乘数加上无符号长整型后缀,显式指定常量类型,把计算语句改成
return val * 1000UL; - 在参与运算前把变量强制转换为长整型,把计算语句改成
return (unsigned long)val * 1000;
内容的提问来源于stack exchange,提问作者15 Volts
相关产品推荐
相关产品推荐

