繁体   English   中英

C中的16位浮点乘法

[英]16bit Float Multiplication in C

我正在做一个小项目,我需要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;
}

顺便说一句:我将浮点数存储在ints中,因为稍后我将尝试将此代码移植到某种不带浮点数的汇编程序中。

问题

为什么代码只对某些值有效? 我忘了一些标准化或类似的东西吗? 还是只是偶然地起作用?

免责声明:我不是CompSci学生,这是一个休闲项目;)

更新#1

多亏了Eric Postpischil的评论,我注意到了代码的一个问题: hidden_bit标志被1掉了(应该是1 << 10 )。 有了这一更改,我不再获得小数位,但是仍然关闭了一些计算(例如3•3=20 )。 我认为,这就是答案中所描述的res_frac偏移。

更新#2

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

从粗略的外观:

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

通过chux的详细介绍:

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

一个问题是您要截断而不是舍入:

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

您应该首先计算res_frac & 0x7ff ,这是算法将要丢弃的22位结果的一部分,并将其与0x400进行比较。 如果在下面,则截断。 如果高于,则从零舍入。 如果等于0x400 ,则四舍五入为偶数。

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

一些或所有浮点算术运算共有许多步骤。 我建议将它们分别提取到一个函数中,该函数可以针对一个问题编写,并分别进行测试。 然后,当您编写乘法时,您只需要处理该操作的细节即可。

使用具有实际带符号指数且全有效位数在更宽的无符号整数字段中的结构,所有操作将更加容易。 如果要处理带符号的数字,则符号位也将具有布尔值。

以下是一些可能是单独功能的示例操作,至少在您开始起作用之前:

解压:采用16位浮点数并将指数和有效位数提取到结构中。

pack:撤消解压缩-处理掉隐藏的部分,对指数施加偏差,然后将它们组合成一个浮点数。

归一化:移位有效位数并调整指数,以将最高有效1位移到指定的位位置。

舍入:应用舍入规则以丢弃低有效位。 如果要舍入为最接近的IEEE 754样式,则需要一个将被丢弃的最高有效位的保护位,以及一个额外的位,指示是否有任何一位的重要性低于保护位。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM