【问题标题】:Need Floating Point Precision Using Unsigned Int需要使用 Unsigned Int 的浮点精度
【发布时间】:2013-12-11 13:28:50
【问题描述】:

但是,我正在使用的微芯片没有浮点精度空间。我需要在某些方程式中考虑小数值。到目前为止,我很幸运地使用了旧的 *100 -> /100 方法,如下所示:

increment = (short int)(((value1 - value2)*100 / totalSteps));

// later in the code I loop through the number of totolSteps
// adding back the increment to arrive at the total I want at the precise time
// time I need it. 
newValue = oldValue + (increment / 100);

这对于 0-255 除以总步数最多为 300 的值非常有效。在 300 之后,小数点右侧的小数值变得很重要,因为它们当然会随着时间的推移而累加。

我很好奇是否有人有更好的方法在整数范式中保存小数精度?我尝试使用 *1000 /1000,但这根本不起作用。

提前谢谢你。

【问题讨论】:

  • 如何让你需要关心的最小增量等于1?那么实际值就是unsigned int 乘以比例因子。将其视为以美分 (0.01) 而非美元/欧元/其他方式计算。
  • 我不确定我是否理解您尝试实现的算法。你是在总结一堆整数 N1/1 + N2/2 + N3/3 + .. + N300/300 吗?无论如何,我建议您学习定点和有理算术(或切换到更大的 MCU。)
  • 我在给定的周期数内创建从第一个值到第二个值的渐变。所以让我们说 0 - 200 / totalSteps = 用于相位 0 到 200 的增量(在子程序中检查其极性 +/- 之后)。本质上,定相必须在给定的周期内准确完成。它正在工作,但是当 totalSteps 超过 300 时,它会跳过至少 2 次迭代。
  • 您不应该尝试使用以 10 为底的定点精度,而应使用以 2 为底的精度。这样做的好处是除法可以通过移位操作完成,而且累积的错误可能表现得更好。
  • 所以我应该存储整个值向左移动了许多精度位置,然后在最后一秒将其移回右侧并转换最终值?这完全有道理。

标签: c floating-point integer


【解决方案1】:

整数分数称为不动点数学。

尝试谷歌搜索“定点”。

固定点提示和技巧超出了SO答案的范围......

示例:5 抽头 FIR 滤波器

// C 是使用 2.8 固定精度的滤波器系数。 // 2 MSB (of 10) 是整数部分,8 LSB (of 10) 是小数部分。 // 这里的实际分数精度是 1/256。

int FIR_5(int* in,    // input samples
          int inPrec, // sample fraction precision
          int* c,     // filter coefficients
          int cPrec)  // coefficients fraction precision
{
    const int coefHalf = (cPrec > 0) ? 1 << (cPrec - 1) : 0; // value of 0.5 using cPrec
    int sum = 0; 
    for ( int i = 0; i < 5; ++i )
    {
        sum += in[i] * c[i];
    }

    // sum's precision is X.N. where N = inPrec + cPrec;
    // return to original precision (inPrec)
    sum = (sum + coefHalf) >> cPrec; // adding coefHalf for rounding
    return sum;
}

int main()
{
    const int filterPrec = 8;
    int C[5] = { 8, 16, 208, 16, 8 }; // 1.0 == 256 in 2.8 fixed point. Filter value are 8/256, 16/256, 208/256, etc.
    int W[5] = { 10, 203, 40, 50, 72}; // A sampling window (example)
    int res = FIR_5(W, 0, C, filterPrec);
    return 0;
}

注意事项:

在上面的例子中:

  • 样本是整数(没有分数)
  • 系数有 8 位小数。
  • 8 位小数表示1 的每次变化都被视为 1/256。 1 &lt;&lt; 8 == 256
  • 有用的符号是 Y.Xu 或 Y.Xs。其中 Y 是分配给整数部分的位数,X 是分配给小数部分的位数。 u/s 表示有符号/无符号。
  • 当将 2 个定点数相乘时,它们的精度(小数位的大小)相加。
  • 示例 A 为 0.8u,B 为 0.2U。 C=A*B。 C为0.10u
  • 除法时,使用移位操作降低结果精度。移动量取决于您。在降低精度之前,最好添加 half 以降低错误。
  • 示例:A=129 in 0.8u,略高于 0.5 (129/256)。我们想要整数部分,所以我们将其右移 8。在此之前,我们要添加一个 half,即 128 (1> 8 --> 1。
  • 如果不加一半,最终结果会出现更大的误差。

【讨论】:

  • 用一个真实的例子更新了答案。
  • 完全理解您提出的解决方案需要一点时间,但我会弄清楚的!感谢您的帮助!
【解决方案2】:

不要使用这种方法。

新范式:不要使用 FP 数学或定点数学进行累加。用整数数学做你的累加和其他方程。任何时候你需要得到一些缩放值,除以你的缩放因子(100),但是用原始的、未缩放的值做“加法”部分。

【讨论】:

  • @Mark Löwe 这就是我在 8 位 PIC 中处理步进控制的方式。
  • 定点和整数数学是一回事,都使用整数运算。
  • @egur 同意,Fixed point math 是一个缩放的整数,只能是一个缩放的 int。关于如何在定点数学中进行除法和舍入(舍入到最接近)等操作可能存在细微的变化,这可能与 C int 除法和舍入(截断为零)不同。这种微小的差异取决于定点数学的实现方式。
【解决方案3】:

如果您真的无法在每一步直接进行插值,这里可以快速尝试精确的合理(布雷森汉姆式)插值版本。

div_t frac_step = div(target - source, num_steps);
if(frac_step.rem < 0) {
    // Annoying special case to deal with rounding towards zero.
    // Alternatively check for the error term slipping to < -num_steps as well
    frac_step.rem = -frac_step.rem;
    --frac_step.quot;
}

unsigned int error = 0;

do {
    // Add the integer term plus an accumulated fraction
    error += frac_step.rem;
    if(error >= num_steps) {
        // Time to carry
        error -= num_steps;
        ++source;
    }
    source += frac_step.quot;
} while(--num_steps);

与定点解决方案相比的一个主要缺点是,如果您使用该函数以不同的步长连续走向移动目标,小数项会在迭代之间四舍五入。

哦,为了记录,您的原始代码在步进时似乎没有正确累积分数,例如无论该步骤执行多少次,加法中的 1/100 增量将始终被截断为 0。相反,您真的想将增量添加到更高精度的定点累加器,然后每次迭代将其除以 100(或者最好右移以除以 2 的幂),以计算整数“位置”。

请注意计算中所需的不同整数类型和范围。乘以 1000 将溢出 16 位整数,除非一项是长整数。仔细计算并跟踪每一步的输入范围和余量,然后选择要匹配的整数类型。

【讨论】:

  • 这是一个很好的建议。你在所有方面都是对的。我把事情移到了 1000 乘数,得到了你所说的溢出。我将在此页面上尝试几个示例(一个是您的示例)。非常感谢所有的帮助!
【解决方案4】:

也许您可以通过保存来模拟浮点行为 它使用 IEEE 754 规范

因此您将尾数、指数和符号保存为 unsigned int 值。

对于计算,您使用尾数和指数等按位加法。 乘法和除法可以用按位加法运算代替。

我认为有很多编程人员可以效仿,但它应该可以工作。

【讨论】:

  • 我相当怀疑国产浮点实现会比编译器供应商提供的小得多,而且 OP 没有空间。具有字节对齐字段等的简化特殊情况格式可能会有所帮助,但定点可能更不容易出错。
  • 是的,你的权利。只是一个建议。我也觉得挺复杂的。但是可以为尾数、指数等应用自定义的位长度,这样它可能满足硬件先决条件。
  • 国产浮点实际上是一种极好的学习体验,也是一个非常有趣的项目,因此我建议它。不幸的是,正确实现 IEEE-754 非常微妙。
【解决方案5】:

问题在于您选择的类型:short int 可能是 16 位宽。这就是为什么大乘数不起作用的原因——你被限制在 +/-32767。假设您的编译器支持它,请使用 32 位 long int。顺便问一下,它是什么芯片,什么编译器?

【讨论】:

  • 是的,我的小芯片有 16 位的 int 限制。 :(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-21
  • 1970-01-01
相关资源
最近更新 更多