我想知道如何在二进制中实现IEEE-754 32位单精度浮点除法,没有除法硬件和没有浮点硬件?
我有移动硬件,加,减和乘法。
我已经使用16位字实现了浮点乘法,加法和减法。
我在专有的多核处理器上实现这些指令,并在汇编中编写代码。事先,我使用matlab来验证我的算法。
我知道我需要减去指数,但我如何在尾数上执行无符号除法?
答案 0 :(得分:3)
取决于你想要的复杂程度。保持相当简单,你可以通过倒数近似来尝试除法。
而不是计算:(n / d)你可以解决:n *(1 / d)。
要做到这一点,你需要使用某种方法来计算倒数,例如,Newton-Raphson使用牛顿方法连续计算除数的倒数的更准确估计值,直到它“足够”准确为止。在进行最后的乘法步骤之前你的目的。
修改
刚看到你的更新。毕竟这可能对你有用,也可能不对你有用!
答案 1 :(得分:2)
如果您的硬件具有足够快的整数乘数(比如4-5个周期),则使用迭代方法计算recip = 1 / divisor可能是最快的方法。然后,您将计算商数为dividend * recip。如果硬件提供具有双倍宽度结果的整数乘法(即完整乘积)或提供两个32位整数乘积的高32位的“mulhi”指令,则对于必要的定点计算非常有用。
您需要动态地重新定标定点计算,以便在中间结果中保留接近32位,以获得精确到舍入后最终结果所需的24位的结果。
最快的方法可能是产生一个9位的起始近似值“约”为1 /除数,然后是倒数的立方收敛迭代:
e = 1 - divisor * approx
e = e * e + e
recip = e * approx + approx
最简单的是预先计算起始近似值并将其存储在256字节的数组中,由除数的位23:16(即尾数的8个最高有效小数位)索引。由于所有近似值都是0x100 ... 0x1FF范围内的数字(对应于0.5到0.998046875),因此不需要存储每个值的最高有效位,因为它将为“1”。只需将0x100添加到检索到的表格元素中即可获得初始近似值的最终值。
如果你不能承受256字节的表存储,另一种产生精确到9位的起始近似的方法是3次多项式,它近似为1 /(1 + f),其中f是除数的小数部分尾数,m。由于IEEE-754已知m为[1.0,2.0],因此f为[0.0,1.0]。
正确的舍入到最近或偶数(如果需要)可以通过除数的初等商的反乘来建立余数,并选择最终商使得剩余最小化。
下面的代码演示了上面讨论的近似原理,使用更简单的倒数情况,根据IEEE-754最近或偶数模式进行适当的舍入,并适当处理特殊情况(零,非正规,无穷大, NaN的)。它假设一个32位IEEE-754单精度浮点数可以从32位无符号整数位传输到32位无符号整数。然后,所有操作都在32位整数上执行。
unsigned int frcp_rn_core (unsigned int z)
{
unsigned int x, y;
int sign;
int expo;
sign = z & 0x80000000;
expo = (z >> 23) & 0xff;
x = expo - 1;
if (x > 0xfd) {
/* handle special cases */
x = z << 1;
/* zero or small denormal returns infinity of like sign */
if (x <= 0x00400000) {
return sign | 0x7f800000;
}
/* infinity returns zero of like sign */
else if (x == 0xff000000) {
return sign;
}
/* convert SNaNs to QNaNs */
else if (x > 0xff000000) {
return z | 0x00400000;
}
/* large denormal, normalize it */
else {
y = x < 0x00800000;
z = x << y;
expo = expo - y;
}
}
y = z & 0x007fffff;
#if USE_TABLE
z = 256 + rcp_tab[y >> 15]; /* approx */
#else
x = y << 3;
z = 0xe39ad7a0;
z = mul_32_hi (x, z) + 0x0154bde4;
z = mul_32_hi (x, z) + 0xfff87521;
z = mul_32_hi (x, z) + 0x00001ffa;
z = z >> 4;
#endif /* USE_TABLE */
y = y | 0x00800000;
/* cubically convergent approximation to reciprocal */
x = (unsigned int)-(int)(y * z); /* x = 1 - arg * approx */
x = mul_32_hi (x, x) + x; /* x = x * x + x */
z = z << 15;
z = mul_32_hi (x, z) + z; /* approx = x * approx + approx */
/* compute result exponent */
expo = 252 - expo;
if (expo >= 0) {
/* result is a normal */
x = y * z + y;
z = (expo << 23) + z;
z = z | sign;
/* round result */
if ((int)x <= (int)(y >> 1)) {
z++;
}
return z;
}
/* result is a denormal */
expo = -expo;
z = z >> expo;
x = y * z + y;
z = z | sign;
/* round result */
if ((int)x <= (int)(y >> 1)) {
z++;
}
return z;
}
函数mul_32_high()是一个占位符,用于特定于机器的操作,它返回两个32位位整数的有符号乘法的高32位。代替机器特定版本的半便携式实现是
/* 32-bit int, 64-bit long long int */
int mul_32_hi (int a, int b)
{
return (int)(unsigned int)(((unsigned long long)(((long long)a)*b)) >> 32);
}
基于表的变体使用的256项互易表可以构造如下:
static unsigned char rcp_tab[256];
for (int i = 0; i < 256; i++) {
rcp_tab[i] = (unsigned char)(((1./(1.+((i+.5)/256.)))*512.+.5)-256.);
}