float 转定点计算加法和乘法

float 浮点数转成定点数计算其加法和乘法

需要注意的是,以下的程序是建立在下述条件为真的情况下的:

(sizeof(float) == 4 && sizeof(long long) == 8 && sizeof(int) == 4) == ture

也就是说,一定要保证float和int是占4个字节(32bits),long long是占8个字节(64bits)的。

1, 浮点数转定点乘法
void FixedPointMul(float&a, float& b, float& res)
{
   int ia = *(int*)(&a);
    int ib = *(int*)(&b);
    //正负0的情况下,直接返回
    if (ia == 0x80000000 || ia == 0x0 || ib == 0x80000000 || ib == 0x0)
    {
        res = 0.0f;
        return;
    }
    //实际尾数还要加上最前面的1,这里用Long long的原因是两个24位数的乘法,最多是48位,已超出int
    long long ma = (ia & 0x7fffff) | 0x800000;
    long long mb = (ib & 0x7fffff) | 0x800000;
    //码位
    int ea = ((ia >> 23) & 0xff) - 0x7f;
    int eb = ((ib >> 23) & 0xff) - 0x7f;
    //mul
    long long mc = ma * mb;//相当于a<< (23-ea) a<<(23-eb)
    //计算阶码
    //先计算最高有效位
    int i = 0;
    long long tmp = mc;
    while (tmp != 0)
    {
        tmp >>= 1;
        i++;//得到MSB的位置
    }

    int ec = i - 1 - (23 - ea + 23 - eb) + 0x7f;
    //再右移mc MSB 到第24位
    //把MSB移位到第24位
    //把MSB移位到第24位
    if (i < 24)
        mc = (mc << (24 - i)) & 0xffffff;
    else
        mc = (mc >> (i - 24)) & 0xffffff;
    //
    //标记两个数的正负
    int sa = 0;
    if ((ia & 0x80000000) ^ (ib & 0x80000000))//xor
        sa = 1;//negative
    //float
    //拼接成float
    int rc = 0x00000000;
    rc = rc | (mc & 0x7fffff);
    rc = rc | ((ec << 23) & 0x7fffffff);
    //判断正负
    if (sa == 1)
        rc = rc | 0x80000000;
    res = *(float*)(&rc);
    return;
}
2, 浮点转定点加法(减法可以认为是加上一个负数)
#ifndef MAX
#define MAX(X,Y) ((X)>(Y) ? (X) : (Y))
#endif
void FixedPointAdd(float& a, float& b, float& res)
{
   int ia = *(int*)(&a);
    int ib = *(int*)(&b);
    //考虑正负0的情况
    if (ia == 0)
    {
        res = b;
        if(ib == 0x80000000)
            res = 0.0f;

        return;
    }
    if (ib == 0)
    {
        res = a;
        if (ia == 0x80000000)
            res = 0.0f;
        return;
    }
    //if ((ia & 0x7f800000 == 0x7f800000) | (ib & 0x7f800000 == 0x7f800000));
    //实际尾数还要加上最前面的1
    long long  ma = (ia & 0x7fffff) | 0x800000;
    long long  mb = (ib & 0x7fffff) | 0x800000;
    //码位
    int ea = ((ia >> 23) & 0xff) - 0x7f;
    int eb = ((ib >> 23) & 0xff) - 0x7f;
    //正负
    int sa = ia & 0x80000000;
    int sb = ib & 0x80000000;
    //移动到48位并加上符号运算
    if (ea > eb)
    {//让较大的数的MSB移动到第48位,较小的数对应的移动
        //这样的话,小数点对齐在哪里?
        ma <<= 24;
        //mb如何移动?//mb少移动:ea与eb的绝对值的差
        mb <<= (24 - std::abs(ea - eb));
    }
    else
    {
        mb <<= 24;
        ma <<= (24 - std::abs(ea - eb));
    }
    //考虑符号
    ma = (sa == 0 ? ma : -ma);
    mb = (sb == 0 ? mb : -mb);
    long long mc = ma + mb;
    int i = 0;
    long long tmp = 0;
    //结果符号
    tmp = 0x8000000000000000;
    int sc = ((mc & tmp) == 0 ? 0 : 1);
    if (sc == 1)//说明结果是负数
    {
        mc = -mc;
    }
    //计算阶码
    //先计算最高有效位
    tmp = mc;
    while (tmp != 0)
    {
        tmp >>= 1;
        i++;//得到MSB的位置
    }
    //code
    int ec = 0x7f;
    if (ea > eb)
        ec += ea + (i - 48);
    else
        ec += eb + (i - 48);
    //把MSB移位到第24位
    if (i < 24)
        mc = (mc << (24 - i)) & 0xffffff;
    else
        mc = (mc >> (i - 24)) & 0xffffff;
    //
    //float
    //拼接成float
    int rc = 0x00000000;
    rc = rc | (mc & 0x7fffff);
    rc = rc | ((ec << 23) & 0x7fffffff);
    //判断正负
    if (sc == 1)
        rc = rc | 0x80000000;
    res = *(float*)(&rc);
    return;
}

最后需要注意的是,float占4个字节的情况下,在二进制情况下,共有24bit数字是精确数字,转换到十进制数, 只有6位有效数字是绝对精确的,所以在两个float数字比较其误差时,应注意,第7位及以后的数字是无意义的。
比如说:12345.67888和12345.6999其实对于float的内存来说是一样的,但是它们之间的绝对误差是:0.02111,达到了2%。
再比如:123456.000和123456.9999,在float的内存上是一样的,但是,其绝对差为0.9999。
所以用绝对差来表示两个float数是否相近,是不合理的。

你可能感兴趣的:(C++)