无法分离 ieee 754 浮点的不同部分

Posted

技术标签:

【中文标题】无法分离 ieee 754 浮点的不同部分【英文标题】:cannot separate the different parts of an ieee 754 floating point 【发布时间】:2022-01-15 01:54:06 【问题描述】:

我目前正在尝试使用 C 位运算符将单精度浮点的不同部分与 IEEE 754 分开。我计划将分离的部分放在一个结构中。我的最终目标是使用位运算符编写算术运算。

然而,我偶然发现了一个小问题,我的结果没有任何意义。我一直无法找到解决此问题的方法,也无法在互联网上找到解决方案。对此的任何见解将不胜感激。

以下是我用过的所有模块。

    //test.c
    #include <stdio.h>
    #include "splicing.h"
    
    int main(void)
    
    
        float a = 5, b = -3, c = 0.1;
        sploat A, B, C;
    
        printf("%f\n%x\n", a, *(unsigned int*) &a);
        printf("%f\n%x\n", b, *(unsigned int*) &b);
        printf("%f\n%x\n\n", c, *(unsigned int*) &c);
    
        splice(a, A);
        splice(b, B);
        splice(c, C);
    
        printf("%f\n%hhu %hhi %x\n\n", a, A.s, A.e, A.m);
        printf("%f\n%hhu %hhi %x\n\n", b, B.s, B.e, B.m);
        printf("%f\n%hhu %hhi %x\n\n", c, C.s, C.e, C.m);
    
        return 0;
    
    
    
    
    
    /*
     * Expected results
     *
     * 5 = 0x40a00000
     *  exp = +2
     *  man = 0x200000 (explicit) 0xa00000 (spliced)
     *  sign = 0
     *
     * -3 = 0xc0400000
     *      exp = +1
     *      man = 0x400000 (explicit) 0xc00000 (spliced)
     *      sign = 1
     *
     * 0.1 = 0x3dccccd
     *  exp = -4
     *  man = 0x4ccccc (explicit) 0xcccccc (spliced)
     *  sign = 0
     */
//splicing.h
typedef struct splicedflt
    unsigned char s;        //sign
    signed char e;      //exponent
    unsigned int m;     //mantissa
 sploat;   //short for spliced float


//unfinished
//Makes inserted sploat reflect inserted float. The problem child I need help with.
int splice(float, sploat);

//splicing.c
int splice(float num, sploat strukt)


    unsigned int raw = *(unsigned int*) &num;   //floats don't allow for bitmagic.

    strukt.s = raw >> 31;
    strukt.e = (raw << 1) >> 24;
    strukt.m = ((raw << 9) >> 9) | 0x1000000;

    return 0;


以下是程序的输出。我不知道为什么这不起作用。

$ gcc test.c
$ ./a.out
5.000000
40a00000
-3.000000
c0400000
0.100000
3dcccccd

5.000000
0 0 0

-3.000000
160 0 5588

0.100000
160 -20 7ffe
$ 

【问题讨论】:

永远不要使用*(unsigned int*) &amp;a。在 C 中,使用(union float f; unsigned u; ) a .uFloatToRepresentation(a),其中FloatToRepresentation 使用static unsigned FloatToRepresentation(float x) unsigned int u; memcpy(&amp;u, &amp;x, sizeof u); return u; 定义。这假定 floatunsigned 的大小相同。 (在 C++ 中,不要使用前者。) 浮点数小数部分的首选术语是“有效位”。 “尾数”是对数的小数部分的旧术语。尾数是对数的(加上尾数乘以表示的数字)。有效数字是线性的(有效数字乘以表示的数字)。 其他问题包括:strukt.m = ((raw &lt;&lt; 9) &gt;&gt; 9) | 0x1000000; 对 0,0 和亚法线数的处理不当。代码不考虑无穷大或非数字。 float, unsigned 的大小可能不同。 【参考方案1】:

(据我所知)您的代码中有三个问题。

第一个非常重要的问题是您将spfloat 结构传递给splice 函数按值;也就是说,将相应值的 副本 赋予该函数,并且该副本被修改 - 原始结构(因此在您的 main 函数中保持不变)。要解决这个问题,请“通过引用”传递这些结构(即,使用指向结构的指针作为参数)。

修复此问题后,您的指数字段将出错,因为 IEEE-754 格式使用 biased exponents - 对于单精度(32 位)浮点数据,您可以通过减去该值来纠正此问题(在大多数情况下)存储值的偏差(127)。

您的unsigned int raw = *(unsigned int*) &amp;num; 行中还存在违反strict aliasing rules 的潜在问题;使用memcpy 函数来防止这种情况发生。

这是您的 splice 函数的修改版本:

int splice(float num, sploat* strukt) // Pass "strukt" as a pointer

    unsigned int raw;
    memcpy(&raw, &num, sizeof(raw)); // Avoid strict aliasing violation
    strukt->s = raw >> 31;
    strukt->e = (signed char)((raw << 1) >> 24) - 127; // Remove the BIAS
    strukt->m = ((raw << 9) >> 9) | 0x1000000;
    return 0;


这是在main 中的调用方式:

int main(void)

    float a = 5, b = -3, c = 0.1f;
    sploat A, B, C;

    //...

    splice(a, &A); // Pass the ADDRESS of each structure...
    splice(b, &B);
    splice(c, &C);

    // ...

    return 0;

【讨论】:

【参考方案2】:

splice(a, A); 形式的调用无法更改 A,因为该调用仅将 A 的值传递给函数。地址或任何其他访问A 的方式都不会传递给函数。

更改splice,使其接受float 参数并返回sploat 值:

sploat splice(float num)

    sploat S;

    unsigned raw = (union  float f; unsigned u; ) num .u;

    S.s = raw >> 31;
    S.e = (raw << 1) >> 24;
    S.m = ((raw << 9) >> 9) | 0x1000000;

    return S;

更改调用以匹配:

    A = splice(a);
    B = splice(b);
    C = splice(c);

【讨论】:

【参考方案3】:

您需要将引用传递给您的结构。目前,您的函数没有修改 strukt 参数,因为它是通过 value 传递的,并且您更改了它的 local 副本。

您还必须避免指针双关语,因为它违反了严格的别名规则。请改用memcpy

int splice(float num, sploat *strukt)


    unsigned raw;
    memcpy(&raw, &num, sizeof(raw));

    strukt -> s = raw >> 31;
    strukt -> e = (raw << 1) >> 24;
    strukt -> m = ((raw << 9) >> 9) | 0x1000000;
    return 0;

    splice(a, &A);
    splice(b, &B);
    splice(c, &C);

PS 我没有修改你的移位逻辑,因为这是你的作业,不是我的。

【讨论】:

您忘记了指数偏差。 @AdrianMole 我实际上没有检查按位逻辑:) OP 必须完成一些工作

以上是关于无法分离 ieee 754 浮点的不同部分的主要内容,如果未能解决你的问题,请参考以下文章

在 IEEE 754 32 位 base-2 浮点系统中可以编码多少个不同的值? [复制]

IEEE 754 二进制浮点数不精确

ieee754单精度浮点数 表示方法

IEEE 754浮点除法或减法本身是不是总是产生相同的值?

IEEE 754的简介

IEEE754二进制浮点数算术标准