c++floating-pointlanguage-lawyerendiannesssingle-precision

Does accessing the 4 bytes of a float break C++ aliasing rules


I need to read the binary content of a file and turn the extracted bytes into single precision floating point numbers. How to do this has already been asked here. That question does have proper answers but I'm wondering whether a particular answer is actually valid C++ code.

That answer gives the following code:

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

Is this actually valid C++ code? I'm not sure whether it violates any aliasing rules.

Note that I'm targeting platforms with big endian where a float is guaranteed to be at least 32 bits long.


Solution

  • Is this actually valid C++ code?

    Potentially yes. It has some pre-conditions:

    You can add a checks to ensure safe failure to compile if the first two don't hold:

    static_assert(std::is_same_v<unsigned char, std::uint8_t>);
    static_assert(sizeof(float) == 4);
    

    I'm not sure whether it violates any aliasing rules.

    unsigned char is excempted of such restrictions. std::uint8_t, if it is defined, is in practice an alias of unsigned char, in which case the shown program is well defined. Technically that's not guaranteed by the rules, but the above check will handle the theoretical case where that doesn't apply.


    float is guaranteed to be at least 32 bits long.

    It must be exactly 32 bits long for the code to work. It must also have exactly the same bit-level format as was on the system where the float was serialised. If it's standard IEE-754 single precision on both ends then you're good; otherwise all bets are off.