C位级Int到浮点转换意外输出

时间:2016-11-28 02:54:34

标签: c floating-point bit-manipulation unsigned-integer

背景
我正在玩比特级编码(这不是功课 - 只是好奇)。我在网上和一本名为Hacker's Delight的书中找到了很多很好的资料,但是我遇到了一个在线问题。

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

How to manually (bitwise) perform (float)x?
How to convert an unsigned int to a 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;
    }

2 个答案:

答案 0 :(得分:3)

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

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

unsigned integer

使用/输出示例

#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;
}

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

答案 1 :(得分:0)

我只是在这里说,因为没有具体关于字节序的问题已得到解决。我们来谈谈它。

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

  2. IEEE-754普遍接受的惯例是字节顺序是big-endian(虽然我认为没有正式的规范,因此没有要求实现遵循它)。这意味着如果要将整数值直接解释为float,则需要以big-endian字节顺序排列。

  3. 所以,你可以使用这种方法结合使用if ,只有当知道系统中浮点数和整数的字节顺序相同时

    在基于英特尔的常见架构上,这是不行的。在这些架构上,整数是小端的,浮点数是大端的。您需要将您的值转换为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或反过来,你只需要担心这一点。

    如果您只想输出表示的位数,那么您不必担心。您只需以有用的形式显示整数,例如hex:

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