SW浮点实现的方法是什么?

时间:2013-08-20 17:43:07

标签: c floating-point motorola

  • 我需要能够在我的工作中使用浮点运算 C中的开发环境(CPU:~12 MHz Motorola 68000)。
  • 标准库不存在,这意味着它是一个简单的C而不是 - 由于其他几个问题它不是gcc
  • 我尝试让SoftFloat库进行编译,另外还有另外一个68k特定的FP库(此时其名称让我失望),但是这个特定平台的依赖关系无法解决 - 主要是由于libc缺陷。
  • 我花了大约8小时试图克服连接问题,直到我知道我无法进一步发展。

但是,只需半小时就能完成并实现以下一组功能,这些功能可以充分满足我的需求。

基本思想是分数和非分数部分都是16位整数,因此没有位操作。 非分式部分的范围为< -32767,32767>。分数部分< -0.9999,+ 0.9999> - 它给我们4位数的精度(足以满足我的浮点需求 - 虽然浪费)。

在我看来,这样可以用来制作一个更快,更小 - 只是2字节 - 大 - 替代版本的浮点数,范围<-99,+ 99&gt;和&lt; -0.9,+ 0.9&gt;

这里的问题是,除了IEEE之外的其他技术是否可以使用定点功能实现基本浮点功能(+ - * /)? 稍后,我将需要一些基本的三角函数,但网上有很多资源。

  • 由于HW有2 MB的RAM,我真的不在乎每个软浮点数是否可以节省2个字节(比如说 - 通过在int中保留9比7比特)。因此 - 4个字节就足够了。
  • 另外,通过简要介绍68k指导手册(以及每条指令的周期成本),我做了一些早期的观察:
    • 位移很慢,除非性能至关重要(这里不是这种情况),我更喜欢轻松调试我的软浮点库而不是5个周期更快的代码。此外,由于这是C而不是68k ASM,很明显速度不是关键因素。
    • 8位操作数和16位一样慢(在大多数情况下给出或取一个周期),因此为了性能而压缩浮点似乎没什么意义。

您建议使用定点在C中实现浮点而不依赖于其他库/代码有哪些改进/方法?

也许可以使用不同的方法同时对压裂和非压裂部件进行操作?

这是代码(仅使用计算器进行测试),请忽略C ++ - 在函数中间的声明和初始化(稍后我会将其重新格式化为C风格):

inline int Pad (int f) // Pad the fractional part to 4 digits
{
if (f < 10) return f*1000;
    else if (f < 100) return f*100;
        else if (f < 1000) return f*10;
            else return f;
}

//  We assume fractional parts are padded to full 4 digits 
inline void Add (int & b1, int & f1,  int b2, int f2)
{
b1 += b2;
f1 +=f2;
if (f1 > 9999) { b1++; f1 -=10000; }
else if (f1 < -9999) { b1--; f1 +=10000; }
f1 = Pad (f1);
}

inline void Sub (int & b1, int & f1,  int b2, int f2)
{
    // 123.1652 - 18.9752 = 104.1900
b1 -= b2; // 105
f1 -= f2; // -8100
if (f1 < 0) { b1--; f1 +=10000; }
f1 = Pad (f1);
}

    // ToDo: Implement a multiplication by float
inline void Mul (int & b1, int & f1, int num)
{
    // 123.9876 * 251 = 31120.8876
b1 *=num;   // 30873
long q = f1*num; //2478876
int add = q/10000; // 247
b1+=add; // 31120
f1 = q-(add*10000);//8876
f1 = Pad (f1);
}
    // ToDo: Implement a division by float
inline void Div (int & b1, int & f1, int num)
{
    // 123.9876 / 25 = 4.959504
int b2 = b1/num; // 4
long q = b1 - (b2*num); // 23
f1 = ((q*10000) + f1) / num; // (23000+9876) / 25 = 9595
b1 = b2;
f1 = Pad (f1);
}

2 个答案:

答案 0 :(得分:0)

您正在考虑使用简单的定点实现的错误基础。如果对小数位使用,则会容易得多。例如使用16位作为整数部分,16位作为小数部分(范围-32767/32767,精度为1/2 ^ 16,这比你的精度高很多)。

最好的部分是加法和减法很简单(只需将两个部分加在一起)。乘法有点棘手:您必须注意溢出,因此有助于在64位中进行乘法运算。您还必须在乘法后移位结果(无论多少位都在十进制中)。

typedef int fixed16;

fixed16 mult_f(fixed16 op1, fixed16 op2)
{
         /* you may need to do something tricky with upper and lower if you don't
          * have native 64 bit but the compiler might do it for us if we are lucky
          */
         uint64_t tmp;
         tmp = (op1 * op2) >> 16;

          /* add in error handling for overflow if you wish - this just wraps */
         return tmp & 0xFFFFFFFF;
}

分部是类似的。

有人可能已经实现了几乎完全符合您的需求(或者可以被黑客攻击以使其正常工作),这被称为libfixmath

答案 1 :(得分:0)

如果您决定使用定点,则整数(即int和小数部分)应该在同一个基数中。如上所述,对于int部分使用二进制和对小数部分使用十进制不是非常优化,并且会减慢计算速度。使用二进制定点,您只需要在执行操作后移动适当的数量,不需要像您的想法那样进行长时间的调整。如果你想使用Q16.16那么libfixmath就像上面提到的dave是个不错的选择。如果您想要不同的精度或浮点位置,例如Q14.18,Q19.13,那么请编写自己的库。

如果你想要更大的范围,那么浮点可能是更好的选择。根据您自己的要求编写库,选择最容易实现且速度最快的格式,除非您打算与其他设备交换数据,否则无需遵循IEEE 754规范。例如,e.s.m的格式,其中7个指数位后跟符号位,然后是24位尾数。指数不需要有偏差,因此要使基数仅需要算术移位25,符号位也将被扩展。但是如果偏移比减法慢,则过量-n更好。