繁体   English   中英

浮点仿真或给定范围内的数字的固定点

[英]Floating point emulation or Fixed Point for numbers in a given range

我有一个没有浮点支持的协处理器。 我试图使用32位修复点,但它无法处理非常小的数字。 我的数字从1到1e-18不等。 一种方法是使用浮点仿真,但速度太慢。 在我们知道数字不会大于1且小于1e-18的情况下,我们能否使它更快。 或者有没有办法让修正点工作在非常小的数字上。

这是不可能对于32位定点编码从10 -18代表数字1。这是一个事实,即从10 -18的跨度为10 18的比例立即明显,但非零的编码一个32位整数跨度的比率小于2 32 ,远小于10 18 因此,没有选择定点编码的比例将提供所需的跨度。

因此,32位定点编码不起作用,您必须使用其他一些技术。

在某些应用中,使用多个定点编码可能是合适的。 也就是说,各种输入值将使用定点编码进行编码,但每个都具有适合它的比例,并且中间值和输出也将具有定制比例。 显然,只有在设计时才能确定合适的尺度,这才有可能。 否则,您应该放弃32位定点编码并考虑替代方案。

简化的24位浮点数是否足够快且足够准确?:

#include <stdio.h>
#include <limits.h>

#if UINT_MAX >= 0xFFFFFFFF
typedef unsigned myfloat;
#else
typedef unsigned long myfloat;
#endif

#define MF_EXP_BIAS 0x80

myfloat mfadd(myfloat a, myfloat b)
{
  unsigned ea = a >> 16, eb = b >> 16;
  if (ea > eb)
  {
    a &= 0xFFFF;
    b = (b & 0xFFFF) >> (ea - eb);
    if ((a += b) > 0xFFFF)
      a >>= 1, ++ea;
    return a | ((myfloat)ea << 16);
  }
  else if (eb > ea)
  {
    b &= 0xFFFF;
    a = (a & 0xFFFF) >> (eb - ea);
    if ((b += a) > 0xFFFF)
      b >>= 1, ++eb;
    return b | ((myfloat)eb << 16);
  }
  else
  {
    return (((a & 0xFFFF) + (b & 0xFFFF)) >> 1) | ((myfloat)++ea << 16);
  }
}

myfloat mfmul(myfloat a, myfloat b)
{
  unsigned ea = a >> 16, eb = b >> 16, e = ea + eb - MF_EXP_BIAS;
  myfloat p = ((a & 0xFFFF) * (b & 0xFFFF)) >> 16;
  return p | ((myfloat)e << 16);
}

myfloat double2mf(double x)
{
  myfloat f;
  unsigned e = MF_EXP_BIAS + 16;
  if (x <= 0)
    return 0;
  while (x < 0x8000)
    x *= 2, --e;
  while (x >= 0x10000)
    x /= 2, ++e;
  f = x;
  return f | ((myfloat)e << 16);
}

double mf2double(myfloat f)
{
  double x;
  unsigned e = (f >> 16) - 16;
  if ((f & 0xFFFF) == 0)
    return 0;
  x = f & 0xFFFF;
  while (e > MF_EXP_BIAS)
    x *= 2, --e;
  while (e < MF_EXP_BIAS)
    x /= 2, ++e;
  return x;
}

int main(void)
{
  double testConvData[] = { 1e-18, .25, 0.3333333, .5, 1, 2, 3.141593, 1e18 };
  unsigned i;
  for (i = 0; i < sizeof(testConvData) / sizeof(testConvData[0]); i++)
    printf("%e -> 0x%06lX -> %e\n",
           testConvData[i],
           (unsigned long)double2mf(testConvData[i]),
           mf2double(double2mf(testConvData[i])));

  printf("300 * 5 = %e\n", mf2double(mfmul(double2mf(300),double2mf(5))));
  printf("500 + 3 = %e\n", mf2double(mfadd(double2mf(500),double2mf(3))));
  printf("1e18 * 1e-18 = %e\n", mf2double(mfmul(double2mf(1e18),double2mf(1e-18))));
  printf("1e-18 + 2e-18 = %e\n", mf2double(mfadd(double2mf(1e-18),double2mf(2e-18))));
  printf("1e-16 + 1e-18 = %e\n", mf2double(mfadd(double2mf(1e-16),double2mf(1e-18))));

  return 0;
}

输出( ideone ):

1.000000e-18 -> 0x459392 -> 9.999753e-19
2.500000e-01 -> 0x7F8000 -> 2.500000e-01
3.333333e-01 -> 0x7FAAAA -> 3.333282e-01
5.000000e-01 -> 0x808000 -> 5.000000e-01
1.000000e+00 -> 0x818000 -> 1.000000e+00
2.000000e+00 -> 0x828000 -> 2.000000e+00
3.141593e+00 -> 0x82C90F -> 3.141541e+00
1.000000e+18 -> 0xBCDE0B -> 9.999926e+17
300 * 5 = 1.500000e+03
500 + 3 = 5.030000e+02
1e18 * 1e-18 = 9.999390e-01
1e-18 + 2e-18 = 2.999926e-18
1e-16 + 1e-18 = 1.009985e-16

减法留作练习。 同样更好的转换例程。

使用64位固定点并完成它。

与32位固定点相比,乘法速度会慢4倍,但它仍然比浮点仿真更有效。

在嵌入式系统中,我建议使用16 + 32,16 + 16,8 + 16或8 + 24位冗余浮点表示,其中每个数字只是M * 2 ^ exp。

在这种情况下,您可以选择用M = 0和exp = 0表示零; 对于每个2的幂,有16-32个表示 - 这主要使比较比通常更难。 也可以例如在减法之后推迟归一化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM