【问题标题】:Embedded C Cortex-M4: Properly handle division by big numbers?嵌入式 C Cortex-M4:正确处理大数除法?
【发布时间】:2019-04-27 14:38:20
【问题描述】:

我正在为光谱学编写嵌入式代码。 为了构建我的光谱,我需要将一个区间(动态范围由问题的物理/规格给出)线性映射到另一个。基本上在处理数据后,我有一系列样本(峰值),每个样本都会对频谱有所贡献(即会增加直方图中特定 bin 的计数器)。 这是一个草图: 所以在 C 语言中,我需要将每个峰值映射到 [0:4095] 中,并且我在 MCU (LPC4370) 上实时执行此操作,因此我需要快速进行。 问题是我的愚蠢实现将所有内容都压缩为 0。 这就是我所做的:

 #define MCA_SIZE     4096
 #define PEAK_MAX     1244672762
 #define PEAK_MIN     6000000

 int32_t mca[MCA_SIZE];
 int32_t peak_val;
 int32_t bin_val;

[...]

 if(peak_val > PEAK_MIN)
      {
       bin_val = (int)(MCA_SIZE*(peak_val-PEAK_MIN)/(PEAK_MAX-PEAK_MIN));

       /*Increment corrispondent multi channel bin*/
       mca[bin_val]+=1;
      };

如果是小写,每个数量都是 int32,#define 是大写。问题是相信这个

(peak_val-PEAK_MIN)/(PEAK_MAX-PEAK_MIN)

经常接近于零。 所以我最终只装满了前一两个箱子。

这是经过几千次迭代后 mca 的第一个值的屏幕截图:

这是正在研究的代码的分解视图,以及断点处的寄存器状态。

处理此类问题的最佳/最快方法是什么?

【问题讨论】:

  • 为什么在计算bin_val之后使用mca[peak_val/1000]+=1;?我本来期望像++mca[bin_val];这样的东西。
  • 你做得对。但是您正在寻找错误的错误位置。您的计算首先完成 MCA_SIZE*(peak_val-PEAK_MIN),然后是 /(PEAK_MAX-PEAK_MIN)。该顺序由标准保证,否则您将始终得到零。如果有疑问,您可以检查它的汇编代码。 (顺便说一句。如果你限制你的峰值也限制上限。)
  • @EOF 是的,抱歉我编辑了答案
  • 对数范围是不可能的吗?您似乎可以使用 FPU。如果做不到这一点,那么 log2 函数在 ALU 上的速度相当快。
  • 嗨@JohnMcFarlane,我需要线性映射。来自datasheet ARM Cortex-M4 实现了一个支持八个区域的内存保护单元、一个硬件浮点单元 (FPU)、调试功能和一个系统滴答计时器。 所以 FPU 就在那里.

标签: c embedded fixed-point integer-division


【解决方案1】:

中间结果(MCA_SIZE*(peak_val-PEAKMIN)) 对于 32 位整数数据类型来说太大了。我将使用 uint64_t 进行这些计算,并将所有常量定义为 const uint64_t 而不是使用 #define,在它们的字面值中添加一个后缀 ULL

【讨论】:

  • 谢谢@Elliot Alderson,这就是问题所在。即使这些工具根本没有抱怨,我也错过了它。您认为有更快/更好的方法来做到这一点吗?
  • 仅考虑您提供的信息,我不知道更好的方法。除法可能是计算中最慢的部分,因此如果您能以某种方式排列数据,使PEAKMAX - PEAKMIN 是 2 的整数幂,那么您可以用右移代替 long int 除法。
  • 是的,我正在考虑这个问题。否则我会在那个划分上失去一些精确度。
【解决方案2】:

请注意,您的代码可能会产生有符号整数溢出,这在标准中是未定义的。

来自 C99 标准 (§3.4.3/1)

未定义行为的一个例子是整数溢出行为

所以我会从那里开始,要么改用无符号,要么使用更宽的类型或更改边界。

另外,正如user6556709 在其评论中提到的,表达式:

(MCA_SIZE*(peak_val-PEAK_MIN)/(PEAK_MAX-PEAK_MIN))

由于这些运算符组的从左到右的关联性(注意括号),因此保证执行如下所示:

((MCA_SIZE*(peak_val-PEAK_MIN))/(PEAK_MAX-PEAK_MIN))

所以始终为零求值的表达式(peak_val-PEAK_MIN)/(PEAK_MAX-PEAK_MIN) 没有执行,表达式(MCA_SIZE*(peak_val-PEAK_MIN)) 是先完成的,所以这不是主要问题。

我建议为peak_val 提供一些未填充垃圾箱的示例。

【讨论】:

  • 感谢@user2162550 的回复。 “未标记”是什么意思?你是说 bin_val != 0 吗?
  • 我的意思是“未填充”,而不是递增。
  • 我不确定您希望我发布什么。我从调试工具中添加了一个截图,希望它是正确的:)
  • @user2162550 如果发生溢出,unsigned 将如何提供帮助?
  • @John McFarlane 它将删除未定义的行为
猜你喜欢
  • 2018-12-04
  • 2019-09-22
  • 1970-01-01
  • 2020-03-22
  • 2020-01-10
  • 2017-01-20
  • 1970-01-01
  • 1970-01-01
  • 2020-11-25
相关资源
最近更新 更多