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数是否相近,是不合理的。