繁体   English   中英

C位电平整数到浮点转换的意外输出

[英]C Bit-Level Int to Float Conversion Unexpected Output

背景:
我在玩位级编码(这不是家庭作业-只是很好奇)。 我在网上和一本名为《 Hacker's Delight》的书中找到了很多很好的资料,但是我遇到了其中一个在线问题。

它要求将整数转换为浮点数。 我使用以下链接作为解决此问题的参考:

如何手动(按位)执行(浮动)x?
如何将unsigned int转换为float?
http://locklessinc.com/articles/i2f/

问题与疑问:
我以为我对这个过程足够了解(我试图在注释中记录该过程),但是当我对其进行测试时,我不理解输出。

测试用例:
float_i2f(2)返回1073741824
float_i2f(3)返回1077936128

我希望看到类似2.0000和3.0000的东西。

我在某个地方搞乱了转换吗? 我以为这可能是一个内存地址,所以我在想也许我错过了访问实际数字所需的转换步骤中的某些内容? 还是我打印不正确? 我正在这样打印我的输出:

printf("Float_i2f ( %d ): ", 3);
printf("%u", float_i2f(3));
printf("\n");

但是我认为打印方法适用于C语言中的无符号值(我习惯于用Java编程)。

感谢您的任何建议。

码:

/*
    * float_i2f - Return bit-level equivalent of expression (float) x
    *   Result is returned as unsigned int, but
    *   it is to be interpreted as the bit-level representation of a
    *   single-precision floating point values.
    *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
    *   Max ops: 30
    *   Rating: 4
    */
    unsigned float_i2f(int x) {
        if (x == 0){
            return 0;
        }

        //save the sign bit for later and get the asolute value of x
        //the absolute value is needed to shift bits to put them
        //into the appropriate position for the float
        unsigned int signBit = 0;
        unsigned int absVal = (unsigned int)x;

        if (x < 0){
            signBit = 0x80000000;
            absVal = (unsigned int)-x;
        }

        //Calculate the exponent
        // Shift the input left until the high order bit is set to form the mantissa.
        // Form the floating exponent by subtracting the number of shifts from 158.
        unsigned int exponent = 158; //158 possibly because of place in byte range

        while ((absVal & 0x80000000) == 0){//this checks for 0 or 1. when it reaches 1, the loop breaks
            exponent--;
            absVal <<= 1;
        }

        //find the mantissa (bit shift to the right)
        unsigned int mantissa = absVal >> 8;

        //place the exponent bits in the right place
        exponent = exponent << 23;

        //get the mantissa
        mantissa = mantissa & 0x7fffff;

        //return the reconstructed float
        return signBit | exponent | mantissa;
    }

继续评论。 您的代码是正确的,并且您只是在查看由IEEE-754单精度浮点数中的位组成的等效 unsigned integer IEEE-754单精度数字格式(由符号,扩展指数和尾数组成)可以解释为float ,或者那些相同的位可以解释为unsigned integer (正好由32位)。 您正在输出浮点数的无符号等效项

您可以通过简单的联合进行确认。 例如:

#include <stdio.h>
#include <stdint.h>

typedef union {
    uint32_t u;
    float f;
} u2f;

int main (void) {

    u2f tmp = { .f = 2.0 };
    printf ("\n u : %u\n f : %f\n", tmp.u, tmp.f);

    return 0;
}

用法/输出示例

$ ./bin/unionuf

 u : 1073741824
 f : 2.000000

如果您还有其他问题,请告诉我。 很高兴看到您的研究产生了正确的浮点转换。 (另请注意有关截断/舍入的第二条评论)

我只是在这里打个招呼,因为还没有解决有关字节顺序的任何问题。 因此,让我们谈谈它。

  1. 使用移位和其他按位运算,原始问题中的值构造与字节序无关。 这意味着无论您的系统是大端还是小端,实际值都是相同的。 区别在于它在内存中的字节顺序。

  2. IEEE-754普遍接受的约定是字节顺序为大端顺序(尽管我相信对此没有正式的规范,因此不需要遵循它的实现方式)。 这意味着,如果您想直接将整数值解释为浮点型,则需要按big-endian字节顺序对其进行布局。

所以,你可以使用这种方式与工会联合当且仅当你知道浮点数和整数的系统上的字节顺序是一样的

在常见的基于Intel的体系结构上,这是不可行的。 在那些体系结构上,整数是低位优先,而浮点数是大位优先。 您需要将您的价值转换为big-endian。 一种简单的方法是重新打包其字节, 即使它们已经是big-endian了

uint32_t n = float_i2f( input_val );
uint8_t char bytes[4] = {
    (uint8_t)((n >> 24) & 0xff),
    (uint8_t)((n >> 16) & 0xff),
    (uint8_t)((n >> 8) & 0xff),
    (uint8_t)(n & 0xff)
};
float fval;
memcpy( &fval, bytes, sizeof(float) );

我要强调的是,如果您试图将整数表示重新解释为float或相反,则只需担心这一点。

如果只想输出位的表示形式,则无需担心。 您可以以十六进制之类的有用形式显示整数:

printf( "0x%08x\n", n );

暂无
暂无

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

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