c - 如何正确实现 float 乘法(软件FP)

标签 c floating-point multiplication addition ieee-754

我的程序是关于一个给定 float 的方法,在这个方法中我想将这些 float 相乘或相加。但不是像 a * b 那样相乘,我想将这些 float 分解为它们的结构,例如符号位、指数的 8 位和尾数的其余位。

我想实现/模拟软件浮点加法和乘法(以了解有关 FP 硬件必须执行的操作的更多信息)。

<小时/>

程序的头部有 segmentation :

    #define  SIGN(x) (x>>31);
    #define  MANT(x) (x&0x7FFFFF);
    #define  EXPO(x) ((x>>23)&0xFF);

    #define SPLIT(x, s, m, e) do {  \
    s = SIGN(x);                    \
    m = MANT(x);                    \
    e = EXPO(x);                    \
    if ( e != 0x00 && e != 0xFF ) { \
      m |= 0x800000;                \
    }                               \
    } while ( 0 )  

    #define BUILD(x, s, m, e) do {               \
        x = (s << 31) | (e<<23) | (m&0x7FFFFF);  \
    } while ( 0 )

主要如下:

    float f = 2.3; 
    float g = 1.8; 
    float h = foo(&f, &g);

计算方法如下:

   float foo(float *a, float *b)  {
   uint32_t ia = *(unsigned int *)a;
   uint32_t ib = *(unsigned int *)b;
   uint32_t result = 0;
   uint32_t signa, signb, signr; 
   uint32_t manta, mantb, mantr; 
   uint32_t expoa, expob, expor; 
   SPLIT(ia, signa, manta, expoa); 
   SPLIT(ib, signb, mantb, expob); 

我已经尝试通过添加指数并乘以尾数来进行乘法,如下所示:

   expor = (expoa -127) + (expob -127) + 127;
   mantr = (manta) * (mantb);
   signr = signa ^ signb;

新 float 的返回和重建:

   BUILD(result, signr, mantr, expor);
   return *(float *)&result;

现在的问题是,结果是错误的。 mantr 甚至需要一个非常低的负数(如果 foo 得到 1.5 和 2.4 mantr 需要 -838860800 并且结果是 2.0000000)。

最佳答案

你不能只截断尾数乘法的结果,你需要取 24 位(在使用低半部分进行舍入之后)并重新标准化(调整指数)。

浮点运算保留最高有效位。整数乘积的最高有效部分是高位;低位是小数点后更远的位。 (术语:它是“二进制点”,而不是“小数点”,因为二进制 float 使用基数 2(二进制),而不是 10(十进制)。)

<小时/>

对于标准化输入,输入尾数中的隐式前导 1 表示用于实现 24 x 24 => 48 的 32x32 => 64 位 uint64_t 产品位尾数乘法的高位将位于 2 个可能位置之一,因此您不需要位扫描来找到它。比较或单位测试就可以了。

对于次正规输入,不能保证这一点,因此您需要检查 MSB 在哪里,例如使用 GNU C __builtin_clzll。 (有许多特殊情况需要处理一个或两个输入不正常,和/或输出不正常。)

参见https://en.wikipedia.org/wiki/Single-precision_floating-point_format有关 IEEE-754 二进制 32 格式的更多信息,包括有效数字的隐含前导 1。

并且请参阅@njuffa的答案,了解实际测试的+工作实现,由于某种原因,该实现将64位操作作为两个32位的一半,而不是让C高效地完成。

<小时/>

此外,return *(float *)&result; 违反了严格别名。仅在 MSVC 上安全。在 C99/C11 中使用 union 或 memcpy 进行类型双关。

关于c - 如何正确实现 float 乘法(软件FP),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55551307/

相关文章:

c - 为什么我不能使用 C 打印此文件中的整个消息?

sql - Postgres 中的乘法子查询

Java 浮点除法的显式类型转换

assembly - RISC-V SiFive HiFive Unleashed FMADD(32) 下溢标志未在次正常结果上设置

javascript - 移位与乘法

python - 将 pandas DataFrame 列与系列相乘

c - 将可变参数列表传递给子函数

c - 如何禁用默认的 Raspberry Pi 2 model B UART 驱动程序/模块?

c - 如何使用DBL_MANT_DIG检查strtod

java - 如何避免浮点错误计算 postgres db 中的平均值并在 java 应用程序中获取它?