C语言中的16位浮点乘法

时间:2013-08-28 15:51:30

标签: c algorithm floating-point computer-science

我正在开发一个小项目,我需要浮点乘法和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学生,它是一个休闲项目;)

更新#1

感谢Eric Postpischil的评论,我注意到代码存在一个问题:hidden_bit标志被一个人关闭(应该是1 << 10)。有了这个改变,我不再获得小数位数,但仍有一些计算结果(例如3•3=20)。我假设,它是res_frac转变,如答案中所描述的那样。

更新#2

代码的第二个问题确实是res_frac转移。在更新#1之后,当得到frac1 * frac2的22位结果时,我得到了错误的结果。我已使用更正的班次语句更新了上面的代码。感谢所有的评论和回答! :)

3 个答案:

答案 0 :(得分:3)

从粗略的看:

  • 未尝试确定产品中高位的位置。两个11位数字,每个高位设置,可以产生21位或22位数字。 (两位数的例子:10 2 •10 2 是100 2 ,三位,但是11 2 •11 2 是1001 2 ,四位。)
  • 结果被截断而不是舍入。
  • 标志被忽略。
  • 输入或输出时不处理次正规数。
  • 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样式,则需要一个保护位,它是将被丢弃的最高位,以及一个额外的位,指示是否存在比保护位更低的重要性位。