1

我需要能够在我的 C 开发环境下使用浮点运算(CPU:~12 MHz 摩托罗拉 68000)。标准库不存在,这意味着它是一个简单的 C 并且不 - 由于其他几个问题,它不是 gcc

我尝试编译 SoftFloat 库和另一个 68k 特定的 FP 库(此时我不知道它的名称),但是对于这个特定平台,它们的依赖关系无法解决 - 主要是由于 libc 的缺陷。我花了大约 8 小时试图克服链接问题,直到我知道我无法再进一步。

但是,只花了半个小时就想出并实现以下一组函数,这些函数充分模拟了我的需要的浮点功能。

基本思想是小数部分和非小数部分都是 16 位整数,因此没有位操作。非小数部分的范围为 [-32767, 32767] 和小数部分 [-0.9999, +0.9999] - 这给了我们 4 位数的精度(足以满足我的浮点需求 - 尽管很浪费)。

在我看来,这可以用来制作更快、更小(仅 2 字节大)的浮点替代版本,范围为 [-99, +99] 和 [-0.9, +0.9]

这里的问题是,除了 IEEE 之外,还有哪些其他技术可以使用定点功能来实现基本的浮点功能 ( + - * /)?

稍后,我将需要一些基本的三角函数,但是网上有很多资源。

  • 由于硬件有 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);
}
4

2 回答 2

1

您正在为简单的定点实现考虑错误的基础。如果您使用小数位的位会容易得多。例如,整数部分使用 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

于 2013-08-21T08:07:51.053 回答
1

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

另请参阅进行定点数学的最佳方法是什么?

如果您想要更大的范围,那么浮点可能是更好的选择。根据自己的要求编写库,选择易于实现且易于在软件中实现良好性能的格式,无需遵循 IEEE 754 规范(由于奇数位数和奇怪的指数,仅硬件实现速度快)位的位置),除非您打算与其他设备交换数据。例如,一种格式为exp.sign.significand7 个指数位,后跟一个符号位,然后是 24 个有效位。指数不需要有偏差,因此要得到基数,只需进行 25 的算术移位,符号位也将被扩展。但是如果移位比减法慢,那么excess-n会更好。

于 2013-08-21T09:14:21.450 回答