我正在尝试在硬件中实现32位浮点硬件分频器,我想知道我是否可以就不同算法之间的一些权衡取得任何建议?
我的浮点单元目前支持乘法和加法/减法,但我不打算将它切换到融合乘法 - 加法(FMA)浮点架构,因为这是一个嵌入式平台,我试图最小化区域使用。
答案 0 :(得分:3)
很久很久以前,我遇到了这个简洁易行的浮动/定点分割算法,用于那个时期的军用FPU:
输入必须是无符号的并且已移位x < y
,且两者都在范围< 0.5 ; 1 >
请勿忘记存储班次sh = shx - shy
与原始标志的差异
找到f
(通过迭代)所以y*f -> 1
....之后x*f -> x/y
这是除法结果
将x*f
移回sh
并恢复结果符号(sig=sigx*sigy)
可以像这样轻松计算x*f
:
z=1-y
(x*f)=(x/y)=x*(1+z)*(1+z^2)*(1+z^4)*(1+z^8)*(1+z^16)...(1+z^2n)
,其中
n = log2(num of fractional bits for fixed point, or mantisa bit size for floating point)
当固定位宽数据类型的z^2n
为零时,您也可以停止。
[Edit2]有一点时间和心情,所以32位IEEE 754 C ++实现
我删除了旧的(bignum)示例,以避免混淆未来的读者(如果需要,它们仍然可以在编辑历史中访问)
//---------------------------------------------------------------------------
// IEEE 754 single masks
const DWORD _f32_sig =0x80000000; // sign
const DWORD _f32_exp =0x7F800000; // exponent
const DWORD _f32_exp_sig=0x40000000; // exponent sign
const DWORD _f32_exp_bia=0x3F800000; // exponent bias
const DWORD _f32_exp_lsb=0x00800000; // exponent LSB
const DWORD _f32_exp_pos= 23; // exponent LSB bit position
const DWORD _f32_man =0x007FFFFF; // mantisa
const DWORD _f32_man_msb=0x00400000; // mantisa MSB
const DWORD _f32_man_bits= 23; // mantisa bits
//---------------------------------------------------------------------------
float f32_div(float x,float y)
{
union _f32 // float bits access
{
float f; // 32bit floating point
DWORD u; // 32 bit uint
};
_f32 xx,yy,zz; int sh; DWORD zsig; float z;
// result signum abs value
xx.f=x; zsig =xx.u&_f32_sig; xx.u&=(0xFFFFFFFF^_f32_sig);
yy.f=y; zsig^=yy.u&_f32_sig; yy.u&=(0xFFFFFFFF^_f32_sig);
// initial exponent difference sh and normalize exponents to speed up shift in range
sh =0;
sh-=((xx.u&_f32_exp)>>_f32_exp_pos)-(_f32_exp_bia>>_f32_exp_pos); xx.u&=(0xFFFFFFFF^_f32_exp); xx.u|=_f32_exp_bia;
sh+=((yy.u&_f32_exp)>>_f32_exp_pos)-(_f32_exp_bia>>_f32_exp_pos); yy.u&=(0xFFFFFFFF^_f32_exp); yy.u|=_f32_exp_bia;
// shift input in range
while (xx.f> 1.0f) { xx.f*=0.5f; sh--; }
while (xx.f< 0.5f) { xx.f*=2.0f; sh++; }
while (yy.f> 1.0f) { yy.f*=0.5f; sh++; }
while (yy.f< 0.5f) { yy.f*=2.0f; sh--; }
while (xx.f<=yy.f) { yy.f*=0.5f; sh++; }
// divider block
z=(1.0f-yy.f);
zz.f=xx.f*(1.0f+z);
for (;;)
{
z*=z; if (z==0.0f) break;
zz.f*=(1.0f+z);
}
// shift result back
for (;sh>0;) { sh--; zz.f*=0.5f; }
for (;sh<0;) { sh++; zz.f*=2.0f; }
// set signum
zz.u&=(0xFFFFFFFF^_f32_sig);
zz.u|=zsig;
return zz.f;
}
//---------------------------------------------------------------------------
我想保持简单,所以它还没有优化。例如,您可以用指数*=0.5
替换所有*=2.0
和inc/dec
...如果您与float
运算符/
上的FPU结果进行比较,这将会有点不太精确,因为大多数FPU以80位内部格式计算,而这种实现仅在32位上。
正如您所看到的,我正在使用FPU +,-,*
。通过使用像
特别是如果你想使用大位宽......
不要忘记实现规范化和/或溢出/下溢校正。