简体   繁体   English

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

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

Background: 背景:
I am playing around with bit-level coding (this is not homework - just curious). 我在玩位级编码(这不是家庭作业-只是很好奇)。 I found a lot of good material online and in a book called Hacker's Delight, but I am having trouble with one of the online problems. 我在网上和一本名为《 Hacker's Delight》的书中找到了很多很好的资料,但是我遇到了其中一个在线问题。

It asks to convert an integer to a float. 它要求将整数转换为浮点数。 I used the following links as reference to work through the problem: 我使用以下链接作为解决此问题的参考:

How to manually (bitwise) perform (float)x? 如何手动(按位)执行(浮动)x?
How to convert an unsigned int to a float? 如何将unsigned int转换为float?
http://locklessinc.com/articles/i2f/ http://locklessinc.com/articles/i2f/

Problem and Question: 问题与疑问:
I thought I understood the process well enough (I tried to document the process in the comments), but when I test it, I don't understand the output. 我以为我对这个过程足够了解(我试图在注释中记录该过程),但是当我对其进行测试时,我不理解输出。

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

I expected to see something like 2.0000 and 3.0000. 我希望看到类似2.0000和3.0000的东西。

Did I mess up the conversion somewhere? 我在某个地方搞乱了转换吗? I thought maybe this was a memory address, so I was thinking maybe I missed something in the conversion step needed to access the actual number? 我以为这可能是一个内存地址,所以我在想也许我错过了访问实际数字所需的转换步骤中的某些内容? Or maybe I am printing it incorrectly? 还是我打印不正确? I am printing my output like this: 我正在这样打印我的输出:

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

But I thought that printing method was fine for unsigned values in C (I'm used to programming in Java). 但是我认为打印方法适用于C语言中的无符号值(我习惯于用Java编程)。

Thanks for any advice. 感谢您的任何建议。

Code: 码:

/*
    * 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;
    }

Continuing from the comment. 继续评论。 Your code is correct, and you are simply looking at the equivalent unsigned integer made up by the bits in your IEEE-754 single-precision floating point number. 您的代码是正确的,并且您只是在查看由IEEE-754单精度浮点数中的位组成的等效 unsigned integer The IEEE-754 single-precision number format (made up of the sign, extended exponent, and mantissa), can be interpreted as a float , or those same bits can be interpreted as an unsigned integer (just the number that is made up by the 32-bits). IEEE-754单精度数字格式(由符号,扩展指数和尾数组成)可以解释为float ,或者那些相同的位可以解释为unsigned integer (正好由32位)。 You are outputting the unsigned equivalent for the floating point number. 您正在输出浮点数的无符号等效项

You can confirm with a simple union. 您可以通过简单的联合进行确认。 For example: 例如:

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

Example Usage/Output 用法/输出示例

$ ./bin/unionuf

 u : 1073741824
 f : 2.000000

Let me know if you have any further questions. 如果您还有其他问题,请告诉我。 It's good to see that your study resulted in the correct floating point conversion. 很高兴看到您的研究产生了正确的浮点转换。 (also note the second comment regarding truncation/rounding) (另请注意有关截断/舍入的第二条评论)

I'll just chime in here, because nothing specifically about endianness has been addressed. 我只是在这里打个招呼,因为还没有解决有关字节顺序的任何问题。 So let's talk about it. 因此,让我们谈谈它。

  1. The construction of the value in the original question was endianness-agnostic, using shifts and other bitwise operations. 使用移位和其他按位运算,原始问题中的值构造与字节序无关。 This means that regardless of whether your system is big- or little-endian, the actual value will be the same. 这意味着无论您的系统是大端还是小端,实际值都是相同的。 The difference will be its byte order in memory. 区别在于它在内存中的字节顺序。

  2. The generally accepted convention for IEEE-754 is that the byte order is big-endian (although I believe there is no formal specification of this, and therefore no requirement on implementations to follow it). IEEE-754普遍接受的约定是字节顺序为大端顺序(尽管我相信对此没有正式的规范,因此不需要遵循它的实现方式)。 This means if you want to directly interpret your integer value as a float, it needs to be laid out in big-endian byte order. 这意味着,如果您想直接将整数值解释为浮点型,则需要按big-endian字节顺序对其进行布局。

So, you can use this approach combined with a union if and only if you know that the endianness of floats and integers on your system is the same . 所以,你可以使用这种方式与工会联合当且仅当你知道浮点数和整数的系统上的字节顺序是一样的

On the common Intel-based architectures this is not okay. 在常见的基于Intel的体系结构上,这是不可行的。 On those architectures, integers are little-endian and floats are big-endian. 在那些体系结构上,整数是低位优先,而浮点数是大位优先。 You need to convert your value to big-endian. 您需要将您的价值转换为big-endian。 A simple approach to this is to repack its bytes even if they are already 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) );

I'll stress that you only need to worry about this if you are trying to reinterpret your integer representation as a float or the other way round. 我要强调的是,如果您试图将整数表示重新解释为float或相反,则只需担心这一点。

If you're only trying to output what the representation is in bits, then you don't need to worry. 如果只想输出位的表示形式,则无需担心。 You can just display your integer in a useful form such as hex: 您可以以十六进制之类的有用形式显示整数:

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

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

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