我正在开发一个小项目,我需要浮点乘法和16位浮点数(半精度)。不幸的是,我遇到了算法的一些问题:
1 * 5 = 5
2 * 5 = 10
3 * 5 = 14.5
4 * 5 = 20
5 * 5 = 24.5
100 * 4 = 100
100 * 5 = 482
const int bits = 16;
const int exponent_length = 5;
const int fraction_length = 10;
const int bias = pow(2, exponent_length - 1) - 1;
const int exponent_mask = ((1 << 5) - 1) << fraction_length;
const int fraction_mask = (1 << fraction_length) - 1;
const int hidden_bit = (1 << 10); // Was 1 << 11 before update 1
int float_mul(int f1, int f2) {
int res_exp = 0;
int res_frac = 0;
int result = 0;
int exp1 = (f1 & exponent_mask) >> fraction_length;
int exp2 = (f2 & exponent_mask) >> fraction_length;
int frac1 = (f1 & fraction_mask) | hidden_bit;
int frac2 = (f2 & fraction_mask) | hidden_bit;
// Add exponents
res_exp = exp1 + exp2 - bias; // Remove double bias
// Multiply significants
res_frac = frac1 * frac2; // 11 bit * 11 bit → 22 bit!
// Shift 22bit int right to fit into 10 bit
if (highest_bit_pos(res_mant) == 21) {
res_mant >>= 11;
res_exp += 1;
} else {
res_mant >>= 10;
}
res_frac &= ~hidden_bit; // Remove hidden bit
// Construct float
return (res_exp << bits - exponent_length - 1) | res_frac;
}
顺便说一下:我将浮点数存储在整数中,因为我会尝试将此代码移植到某种没有浮点操作的汇编程序。
为什么代码仅适用于某些值?我忘记了一些规范化或类似的吗?或者它只是偶然起作用?
免责声明:我不是CompSci学生,它是一个休闲项目;)
感谢Eric Postpischil的评论,我注意到代码存在一个问题:hidden_bit
标志被一个人关闭(应该是1 << 10
)。有了这个改变,我不再获得小数位数,但仍有一些计算结果(例如3•3=20
)。我假设,它是res_frac
转变,如答案中所描述的那样。
代码的第二个问题确实是res_frac
转移。在更新#1之后,当得到frac1 * frac2
的22位结果时,我得到了错误的结果。我已使用更正的班次语句更新了上面的代码。感谢所有的评论和回答! :)
答案 0 :(得分:3)
从粗略的看:
11
在一个地方被硬编码为移位金额。这可能是不正确的;正确的数量取决于有效数据如何处理以进行标准化和舍入。fraction_length
。在编码中,它向左移动bits - exponent_length - 1
。为避免错误,应在两个地方使用相同的表达式。从chux更详细的了解:
res_frac = frac1 * frac2
小于23位(产品为22,产品为1),则int
会失败。答案 1 :(得分:1)
一个问题是你要截断而不是舍入:
res_frac >>= 11; // Shift 22bit int right to fit into 10 bit
您应首先计算res_frac & 0x7ff
,即算法即将丢弃的22位结果部分,并将其与0x400
进行比较。如果它在下面,则截断。如果它在上面,则从零开始。如果它等于0x400
,则舍入到偶数替代。
答案 2 :(得分:1)
这是一个关于如何更容易地使代码正确的建议,而不是分析现有代码的错误。
对于某些或所有浮点算术运算,有许多共同的步骤。我建议将每个函数提取到一个函数中,该函数可以专注于一个问题,并单独测试。然后,当你来写,例如乘法,你只需要处理该操作的细节。
使用具有实际有符号指数的结构以及更宽的无符号整数字段中的完整有效数,所有操作将更容易。如果你正在处理带符号的数字,它也会有符号位的布尔值。
以下是一些可以作为单独函数的示例操作,至少在您开始工作之前是这样的:
unpack:取16位浮点数并将指数和有效数字提取到结构中。
pack:撤消解包 - 处理丢弃隐藏位,应用expoent的偏差,并将它们组合成浮点数。
normalize:移位有效数并调整指数,使最高位1位到指定的位位置。
round:应用舍入规则以删除低有效位。如果要进行最接近的IEEE 754样式,则需要一个保护位,它是将被丢弃的最高位,以及一个额外的位,指示是否存在比保护位更低的重要性位。