C 软件浮点实现的方法有哪些?

C 软件浮点实现的方法有哪些?,c,floating-point,fixed-point,68000,C,Floating Point,Fixed Point,68000,我需要能够在我的C开发环境下使用浮点算法(CPU:~12 MHz Motorola 68000)。标准库不存在,这意味着它是一个基本的C语言,没有-由于其他几个问题,它不是gcc 我尝试编译SoftFloat库和另一个68k特定的FP库(我现在不知道它的名称),但它们的依赖关系无法在这个特定的平台上得到解决-主要是由于libc的缺陷。我花了大约8个小时试图克服链接问题,直到我知道我不能再进一步了 然而,仅仅花了半个小时就想出并实现了以下一组函数,这些函数充分模拟了浮点功能,满足了我的需要 其基本

我需要能够在我的C开发环境下使用浮点算法(CPU:~12 MHz Motorola 68000)。标准库不存在,这意味着它是一个基本的C语言,没有-由于其他几个问题,它不是gcc

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

然而,仅仅花了半个小时就想出并实现了以下一组函数,这些函数充分模拟了浮点功能,满足了我的需要

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

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

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

稍后,我将需要一些基本的三角学,但网上有很多关于这方面的资源

  • 由于硬件有2MB的RAM,我并不在乎是否可以在每个软浮点中保存2个字节(比如,在int中保留9位和7位)。因此-4字节就足够了
  • 此外,通过简单地查看68k说明手册(以及每种说明的周期成本),我也做了一些早期观察:
    • 位移动很慢,除非性能至关重要(这里不是这种情况),否则我更喜欢轻松调试我的软浮点库,而不是快5个周期的代码。此外,由于这是C,而不是68k ASM,速度显然不是一个关键因素
    • 8位操作数的速度与16位一样慢(在大多数情况下,给定或获取一个周期),因此,为了性能起见压缩浮点值似乎没有多大意义
对于在不依赖其他库/代码的情况下使用定点在C中实现浮点,您会提出哪些改进/方法

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

这里是代码(只使用计算器测试),请忽略函数中的C++类声明和初始化(稍后我将重新格式化为C样式):

inline int Pad(int f)//将小数部分填充到4位
{
如果(f<10)返回f*1000;
否则如果(f<100)返回f*100;
否则如果(f<1000)返回f*10;
否则返回f;
}
//我们假设小数部分填充为完整的4位数字
内联无效添加(内联b1、内联f1、内联b2、内联f2)
{
b1+=b2;
f1+=f2;
如果(f1>9999){b1++;f1-=10000;}
如果(f1<-9999){b1--;f1+=10000;}
f1=垫(f1);
}
内联空心接头(内联b1、内联f1、内联b2、内联f2)
{
// 123.1652 - 18.9752 = 104.1900
b1-=b2;//105
f1-=f2;//-8100
如果(f1<0){b1--;f1+=10000;}
f1=垫(f1);
}
//ToDo:通过浮点实现乘法
内联void Mul(int&b1、int&f1、int-num)
{
// 123.9876 * 251 = 31120.8876
b1*=num;//30873
长q=f1*num;//2478876
int add=q/10000;//247
b1+=add;//31120
f1=q-(加*10000);//8876
f1=垫(f1);
}
//ToDo:通过浮点实现除法
内联void Div(int&b1、int&f1、int-num)
{
// 123.9876 / 25 = 4.959504
int b2=b1/num;//4
长q=b1-(b2*num);//23
f1=((q*10000)+f1)/num;//(23000+9876)/25=9595
b1=b2;
f1=垫(f1);
}

对于简单的定点实现,您的想法是错误的。如果你用位来表示小数点,那就容易多了。e、 g.整数部分使用16位,小数部分使用16位(范围-32767/32767,精度为1/2^16,比您的精度高得多)

最好的部分是加法和减法很简单(只需将两部分相加)。乘法有点棘手:您必须注意溢出,因此它有助于64位乘法。您还必须在乘法后移动结果(按十进制中的位数)

分工是相似的


有人可能已经实现了几乎完全符合您需要的(或者可以通过黑客攻击使其工作)即所谓的

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

另见

如果您想要更大的范围,那么浮点可能是更好的选择。根据您自己的要求编写一个库,选择一种易于实现且易于在软件中实现良好性能的格式,无需遵循IEEE 754规范(由于奇数位数和奇怪指数位的位置,硬件实现速度很快),除非您打算与其他设备交换数据。例如,带有7个指数位的
exp.sign.magnifid
格式
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);
}
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;
}