【问题标题】:Arduino: float function returns infArduino:float函数返回inf
【发布时间】:2016-06-03 18:42:16
【问题描述】:

我有一个需要一些建议的功能(如下所示)。该函数返回拟合(通过最小二乘法)到 n 个数据点的直线的斜率。为了给你一个背景,我的项目是一个基于气压的高度计,它使用这个函数根据最近的 n 个高度-时间对来确定速度。这些高度-时间对存储在 2 个全局数组中(times[]alts[])。

我的问题不是这种方法不起作用。它通常会。但有时我会运行高度计,这个函数会返回值'inf',其中穿插着一堆其他错误值(我也见过'NaN',但这种情况更罕见)。在这一点上,我有几个方面的怀疑,但我想要一个新的视角。以下是一些可能有用也可能没用的更多上下文信息:

  • 我正在为正交编码器使用中断
  • times[] 数组的类型为 unsigned long
  • alts[] 数组的类型为 float
  • nconst int,在本例中为 n = 9
  • 在 ATMEGA328 上,doublefloat..Arduino-double 相同

    float velF() { // uses the last n data points, fits a line to them, 
                   // and uses the slope of that line as the velocity at that moment
         float sumTY = 0, sumT = 0, sumY = 0, sumT2 = 0;
         for (int i = 0; i < n; i++) {
             sumTY += (float)times[i] * alts[i] / 1000;
             sumT += (float)times[i] / 1000;
             sumY += alts[i];
             sumT2 += (float)times[i] * times[i] / 1000000;
         }
         return (n*sumTY - sumT*sumY) / (n*sumT2 - sumT*sumT);
    }
    

任何帮助或建议将不胜感激!

【问题讨论】:

  • 你那里有一个部门。你检查过分母为零吗?
  • 当 OP 说 “有时我会运行高度计,这个函数会返回值 'inf' 并穿插着一堆其他错误值” 你在说高度计吗界面?
  • @Ben Stringer 我不认为你理解:插入一些代码来查看分母 是零还是接近零,以消除你对它的假设“应该”。如果失败,那么问题出在其他地方,或者算法不正确。所以转储数组内容并在发生时检查它们。
  • 如果除法产生inf,那么只有四种可能:(1)被除数是inf; (2) 除数为0; (3) 编译器产生了不正确的代码; (4) 硬件损坏。最后两个不太可能。第一个似乎与代码不一致。这留下了一个零除数作为最可能的解释。

标签: arduino floating-point least-squares divide-by-zero


【解决方案1】:

代码肯定会被零除。

由于各种原因,n*sumT2 - sumT*sumT 将为零。 @John Bollinger 在大多数情况下,除法的顶部(股息)也将为零,返回值为零是可以接受的。

float velF(void) {
     float sumTY = 0, sumT = 0, sumY = 0, sumT2 = 0;
     for (size_t i = 0; i < n; i++) {

         // insure values are reasoable
         assert(alts[i]  >= ALT_MIN  && alts[i]  <= ALT_MAX);
         assert(times[i] >= TIME_MIN && times[i] <= TIME_MAX);

         sumTY += (float)times[i] * alts[i] / 1000;
         sumT += (float)times[i] / 1000;
         sumY += alts[i];
         sumT2 += (float)times[i] * times[i] / 1000000;
     }

     float d = n*sumT2 - sumT*sumT;
     if (d == 0) return 0;
     return (n*sumTY - sumT*sumY) / d;
}

旁注:可以考虑除法以提高准确性和速度。建议以double 执行最后一次计算。

float velF(void) {
     float sumTY = 0, sumT = 0, sumY = 0, sumT2 = 0;
     for (size_t i = 0; i < n; i++) {
         float tf = (float) times[i];
         sumTY += tf * alts[i];
         sumT += tf;
         sumY += alts[i];
         sumT2 += tf * tf;
     }

     double nd = n; 
     double sumTd = sumT; 
     double d = nd*sumT2 - sumTd*sumTd;
     if (d == 0) return 0;
     return (nd*sumTY - sumTd*sumY)*1000 / d;
}

【讨论】:

  • 我非常确信它正在执行除以零。 @chux 的建议将是隔离分母并通过一些好的旧 Serial.print() 调试来确认这一理论的绝佳方法。这将使我能够接近真正问题的答案,即:为什么此功能有时有效,而其他功能无效?我根本无法“抛弃”返回“inf”的值。程序认为我的速度无限大的事实表明存在更深层次的问题。当我找到它时,我一定会发布答案!
  • 附注上的附注:我的印象是,在 ATMEGA328 上,双精度与浮点数相同? arduino.cc/en/Reference/Double
  • @Ben Stringer 我的double 建议是针对通用平台的。似乎对您没有任何好处。
  • 我测试过,代码执行除以0,但它仍然不应该!我从来没有见过这样的事情。当分母变为 0 时,我让它打印出变量 sumT 和 sumT2 以查看分母是否会变为 0。但事实并非如此。它应该是 13 或 14 之类的东西,但它仍然报告为 0!对这些新信息有何想法?
  • @Ben Stringer 1) ".. 正在执行除以 0,但它仍然不应该" --> 为什么不呢?输入的值是多少? 2)“让它打印出变量”-->你使用什么格式"%f""%e"?建议"%e"。 3)您是否使用此答案代码的变体?
猜你喜欢
  • 2021-04-09
  • 2021-02-13
  • 1970-01-01
  • 2020-05-26
  • 2020-01-24
  • 2016-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多