Building a 32-bit float out of its 4 composite bytes

前端 未结 6 914
渐次进展
渐次进展 2020-11-27 05:10

I\'m trying to build a 32-bit float out of its 4 composite bytes. Is there a better (or more portable) way to do this than with the following method?

#includ         


        
相关标签:
6条回答
  • 2020-11-27 05:42

    I typically use this in C -- no memcpy or union required. It may break aliasing rules in C++, I don't know.

    float bytesToFloat(uint8_t *bytes, bool big_endian) {
        float f;
        uint8_t *f_ptr = (uint8_t *) &f;
        if (big_endian) {
            f_ptr[3] = bytes[0];
            f_ptr[2] = bytes[1];
            f_ptr[1] = bytes[2];
            f_ptr[0] = bytes[3];
        } else {
            f_ptr[3] = bytes[3];
            f_ptr[2] = bytes[2];
            f_ptr[1] = bytes[1];
            f_ptr[0] = bytes[0];
        }
        return f;
    }
    

    If you have a whole array of bytes that need to be re-interpreted as floats, you can call the following procedure for each consecutive sequence of 4 bytes in the array if necessary, to switch the byte order (e.g. if you are running on a little endian machine, but the bytes are in big endian order). Then you can simply cast the uint8_t * array pointer to float *, and access the memory as an array of floats.

    void switchEndianness(uint8_t *bytes) {
        uint8_t b0 = bytes[0];
        uint8_t b1 = bytes[1];
        uint8_t b2 = bytes[2];
        uint8_t b3 = bytes[3];
        bytes[0] = b3;
        bytes[1] = b2;
        bytes[2] = b1;
        bytes[3] = b0;
    }
    
    0 讨论(0)
  • 2020-11-27 05:44

    You could use a memcpy (Result)

    float f;
    uchar b[] = {b3, b2, b1, b0};
    memcpy(&f, &b, sizeof(f));
    return f;
    

    or a union* (Result)

    union {
      float f;
      uchar b[4];
    } u;
    u.b[3] = b0;
    u.b[2] = b1;
    u.b[1] = b2;
    u.b[0] = b3;
    return u.f;
    

    But this is no more portable than your code, since there is no guarantee that the platform is little-endian or the float is using IEEE binary32 or even sizeof(float) == 4.

    (Note*: As explained by @James, it is technically not allowed in the standard (C++ §[class.union]/1) to access the union member u.f.)

    0 讨论(0)
  • 2020-11-27 05:51

    You can use std::copy:

    float bytesToFloat(uchar b0, uchar b1, uchar b2, uchar b3) 
    { 
        uchar byte_array[] = { b3, b2, b1, b0 };
        float result;
        std::copy(reinterpret_cast<const char*>(&byte_array[0]),
                  reinterpret_cast<const char*>(&byte_array[4]),
                  reinterpret_cast<char*>(&result));
        return result;
    } 
    

    This avoids the union hack, which isn't technically allowed by the language. It also avoids the commonly used reinterpret_cast<float*>(byte_array), which violates the strict aliasing rules (it is permitted to reinterpret any object as an array of char, so the reinterpret_casts in this solution do not violate the strict aliasing rules).

    It still relies on float being four bytes in width and relies on your four bytes being a valid floating point number in your implementation's floating point format, but you either have to make those assumptions or you have to write special handling code to do the conversion.

    0 讨论(0)
  • 2020-11-27 06:00

    The following functions pack/unpack bytes representing a single precision floating point value to/from a buffer in network byte order. Only the pack method needs to take endianness into account since the unpack method explicitly constructs the 32-bit value from the individual bytes by bit shifting them the appropriate amount and then OR-ing them together. These functions are only valid for C/C++ implementations that store a float in 32-bits. This is true for IEEE 754-1985 floating point implementations.

    // unpack method for retrieving data in network byte,
    //   big endian, order (MSB first)
    // increments index i by the number of bytes unpacked
    // usage:
    //   int i = 0;
    //   float x = unpackFloat(&buffer[i], &i);
    //   float y = unpackFloat(&buffer[i], &i);
    //   float z = unpackFloat(&buffer[i], &i);
    float unpackFloat(const void *buf, int *i) {
        const unsigned char *b = (const unsigned char *)buf;
        uint32_t temp = 0;
        *i += 4;
        temp = ((b[0] << 24) |
                (b[1] << 16) |
                (b[2] <<  8) |
                 b[3]);
        return *((float *) &temp);
    }
    
    // pack method for storing data in network,
    //   big endian, byte order (MSB first)
    // returns number of bytes packed
    // usage:
    //   float x, y, z;
    //   int i = 0;
    //   i += packFloat(&buffer[i], x);
    //   i += packFloat(&buffer[i], y);
    //   i += packFloat(&buffer[i], z);
    int packFloat(void *buf, float x) {
        unsigned char *b = (unsigned char *)buf;
        unsigned char *p = (unsigned char *) &x;
    #if defined (_M_IX86) || (defined (CPU_FAMILY) && (CPU_FAMILY == I80X86))
        b[0] = p[3];
        b[1] = p[2];
        b[2] = p[1];
        b[3] = p[0];
    #else
        b[0] = p[0];
        b[1] = p[1];
        b[2] = p[2];
        b[3] = p[3];
    #endif
        return 4;
    }
    
    0 讨论(0)
  • 2020-11-27 06:01

    There's no way to do this portable, since different platforms can use:

    • different byte ordering (big endian vs. little endian)
    • different representations for floating point values (see http://en.wikipedia.org/wiki/IEEE_754-1985 for an example)
    • different sizes for floating point values

    I also wonder where you get these 4 bytes from?

    If I assume that you get them from another system, and you can guarantee that both systems use exactly the same method to store floating-point values in memory, you can use the union trick. Otherwise, your code is almost guaranteed to be non-portable.

    0 讨论(0)
  • 2020-11-27 06:01

    If you want a portable way to do this, you'll have to write a bit of code to detect the endianess of the system.

    float bytesToFloatA(uchar b0, uchar b1, uchar b2, uchar b3)
    {
        float output;
    
        *((uchar*)(&output) + 3) = b0;
        *((uchar*)(&output) + 2) = b1;
        *((uchar*)(&output) + 1) = b2;
        *((uchar*)(&output) + 0) = b3;
    
        return output;
    }
    
    
    float bytesToFloatB(uchar b0, uchar b1, uchar b2, uchar b3)
    {
        float output;
    
        *((uchar*)(&output) + 3) = b3;
        *((uchar*)(&output) + 2) = b2;
        *((uchar*)(&output) + 1) = b1;
        *((uchar*)(&output) + 0) = b0;
    
        return output;
    }
    
    float (*correctFunction)(uchar b0, uchar b1, uchar b2, uchar b3) = bytesToFloatA;
    
    if ((*correctFunction)(0x3e, 0xaa, 0xaa, 0xab) != 1.f/3.f) // horrifying, I know
    {
      correctFunction = bytesToFloatB;
    }
    
    0 讨论(0)
提交回复
热议问题