【问题标题】:16bit Float Multiplication in CC语言中的16位浮点乘法
【发布时间】:2013-08-31 19:24:05
【问题描述】:

我正在做一个小项目,我需要浮点乘法与 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 &lt;&lt; 10)。随着这一变化,我不再有小数位,但仍然有一些计算是关闭的(例如3•3=20)。我假设,这是答案中描述的res_frac 班次。

更新 #2

代码的第二个问题确实是res_frac 移位。更新 #1 后,当我得到 frac1 * frac2 的 22 位结果时,我得到了错误的结果。我已经用更正的 shift 语句更新了上面的代码。感谢大家的每一个评论和回答! :)

【问题讨论】:

  • 我建议您使用调试器(或添加打印语句)来跟踪 all 中间变量的值,以查找无法按预期工作的输入之一。跨度>
  • 你有没有在没有至少一个输入的情况下得到正确的答案?错误总是丢失结果中最不重要的 1 吗?
  • 好的,那么哪个中间变量不符合您的预期?
  • 我在how to debug 上有网页。这不是关于放入打印语句的机制,而是关于如何选择打印/调试操作并使用它们来找出问题所在。
  • @m--s:您如何计算 3•5 的有效位数应为 1001000000?整数三为 11,其归一化有效数为 11000000000(带前导位)。整数 5 是 101,它的归一化有效数字是 10100000000。将它们相乘产生 111100000000000000000。归一化产生 11110000000.0000000000(移位 10 位)。舍入产生 11110000000。删除前导位产生 1110000000。

标签: c algorithm floating-point computer-science


【解决方案1】:

一个问题是你正在截断而不是四舍五入:

res_frac >>= 11;            // Shift 22bit int right to fit into 10 bit

您应该首先计算res_frac &amp; 0x7ff,即您的算法即将丢弃的22 位结果部分,并将其与0x400 进行比较。如果低于,则截断。如果高于,则从零四舍五入。如果等于0x400,则舍入到偶数。

【讨论】:

    【解决方案2】:

    这更多是关于如何更轻松地让您的代码正确的建议,而不是分析现有代码的问题。

    部分或全部浮点算术运算有许多共同的步骤。我建议将每一个提取到一个函数中,该函数可以专注于一个问题来编写,并单独测试。然后当你来写例如乘法,你只需要处理那个操作的细节。

    使用具有实际有符号指数的结构和更宽的无符号整数字段中的完整有效数字,所有操作都将更容易。如果你在处理带符号的数字,它也会有一个布尔值作为符号位。

    以下是一些示例操作,它们可能是单独的函数,至少在您开始工作之前:

    解包:取一个 16 位浮点数并将指数和有效数提取到一个结构中。

    pack: Undo unpack - 处理删除隐藏位,将偏差应用于指数,并将它们组合成一个浮点数。

    normalize:移动有效位并调整指数以将最高有效 1 位带到指定的位位置。

    round:应用您的舍入规则删除低重要性位。如果你想进行 IEEE 754 风格的舍入到最近,你需要一个保护位,它是将被丢弃的最高有效位,以及一个额外的位,指示是否有任何一个位的重要性低于保护位。

    【讨论】:

      【解决方案3】:

      粗略一看:

      • 没有尝试确定产品中高位的位置。两个 11 位的数字,每个都设置了高位,可以产生一个 21 位或 22 位的数字。 (以两位数为例:102•102 是 1002,三位,但 112•112 是 10012,四位。)
      • 结果被截断而不是四舍五入。
      • 符号被忽略。
      • 在输入或输出时不处理次正规数。
      • 11 被硬编码为一个位置的移位量。这可能是不正确的;正确的数量取决于如何处理有效数字以进行归一化和舍入。
      • 在解码中,指数字段右移fraction_length。在编码中,它左移bits - exponent_length - 1。为避免出现错误,应在两个地方使用相同的表达式。

      来自chux的更详细的查看:

      • 如果 int 小于 23 位(22 位用于乘积,1 位用于符号),res_frac = frac1 * frac2 将失败。

      【讨论】:

      • 关于符号:我只使用正数,所以我省略了符号。
      • 如果 sizeof(int) == 2,res_frac = frac1 * frac2; 也会显着失败。
      猜你喜欢
      • 1970-01-01
      • 2015-05-10
      • 1970-01-01
      • 2015-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-14
      相关资源
      最近更新 更多