【问题标题】:prevent arithmetic overflow防止算术溢出
【发布时间】:2020-06-19 15:59:42
【问题描述】:

我想使用 PIC18F14K50 的 ADC 外设计算电压。结果范围在 0-1023(10 位)之间。所以我用了这个简单的计算:

uint16_t voltage = ADC_Result * 5000 / 1023;

但是,结果不正确。我猜发生了算术溢出。我尝试了许多括号的组合,改变元素的顺序等。
使用下面的代码,当ADC_Result 为 1023 时,最好的结果是 4088;距离 5000 还差得很远。

uint16_t voltage = ADC_Result * (5000 / 1023);

我应该怎么做才能在上述计算中获得更好的结果?请不要建议浮点,因为它们会在 MCU 中造成灾难!他们使用大量资源却没有任何实际好处。

【问题讨论】:

  • 有两种方法可以解决这个问题。正如已经建议的那样,使用更宽的整数,或者,如果您可以承受一些精度和分辨率的损失,请先将输入值除以 10 或 100 来缩小输入值。如果我有更多的答案,我会在答案中举个例子时间,但是您可能可以自己计算并计算出细节,或者有人会发现这一点并为我们写下来。

标签: c pic xc8 arithmetic-overflow


【解决方案1】:

您可以为中间乘法使用更宽的类型,例如:

uint16_t voltage = (uint32_t)ADC_Result * 5000 / 1023;

编辑

如果除以 1023 太慢,您可以通过将 5000 / 1023 更改为 5005 / 1024 来获得大致相等的转换,这可以使用快速位移进行除法:

uint16_t voltage = (uint32_t)ADC_Result * 5005 >> 10;

注意1023 * 5005 / 1024 ≃ 5000.1123

【讨论】:

  • 当 ADC_Result = 1023 时,您的代码的结果是 4995,比 4088 好得多!
  • @AmirSinaMashayekh 这很奇怪。上面的代码应该将 1023 的 ADC_Result 值转换为正好 5000 的电压值。(啊!我认为你的意思是 ADC_Result = 1022。)
  • 请记住,32 位除法在 8 位微控制器上非常慢。如果人类不需要它,则根本不要转换它。
  • @IanAbbott 对不起,你是对的!那是我的错误!
  • @AmirSinaMashayekh 如果除以 1023 太慢,您可以改为除以 1024,因为这可以通过简单的位移来完成。您可以通过乘以 5005 而不是 5000 来补偿。例如uint16_t voltage = (uint32_t)ADC_Result * 5005 >> 10;
【解决方案2】:

您应该为此计算使用更宽的整数类型,例如uint32_t

在您的情况下,1023 * 5000 == 3192(因为实际结果 5115000 不适合),所以这是不正确的。 5000 / 1023 == 4,这是整数除法的预期结果。将ADC_Result 除以 1023 将产生相同的行为。

您可以将其计算为uint32_t,然后检查它是否适合uint16_t

uint32_t result_tmp = ADC_Result * (5000 / 1023);
uint16_t result;

if (result > 0xffff) {
    // this won't fit
} else {
    result = (uint16_t) result_tmp;
}

【讨论】:

    【解决方案3】:

    我应该怎么做才能在上面的计算中得到更好的结果?

    OP 的代码溢出 1`6 位数学。


    要获得正确且四舍五入的结果,请使用更广泛的数学和偏移量。

    // uint16_t voltage = ADC_Result * 5000 / 1023;
    uint16_t voltage = (ADC_Result * 5000LU + 1024u/2) / 1024u;
    // or
    #include <stdint.h>
    ...
    uint16_t voltage = (ADC_Result * UINT32_C(5000) + 1024u/2) / 1024u;
    

    5000LU 中的 L 至少提供 32 位数学运算。

    考虑到ADC_Result 不是负数,使用U 可能更简单/更快的数学和更简单的舍入。

    + 1024/2 影响最接近的舍入而不是截断。

    考虑到 A/D 转换器的常见特性,使用 1024 而不是 1023 来正确缩放。附带好处:更快的除法,因为 1024 是 2 的幂。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-23
      • 1970-01-01
      相关资源
      最近更新 更多