Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Puning... Back where I started with unions



  • We use unions to convert between int32_t and float. This is done for some canbus code. I had read there are issues with this and researched it. I found that despite it being recommended against most modern C++ compilers support this. So I am really confused. The recommended way of using reinterpret_cast ends up working with raw pointers when converting to byte arrays. The memcpy way seems to be a bit cleaner, but you have to copy the data. I then did an experiment and I get zero warnings from clang or the compiler (I am sure if I turned something on it might complain) with default Qt settings for gcc. It seems to me the cleanest way to do this is still unions.

        {
            // puning
            qInfo() << "Puning:";
    
            using namespace std;
    
            union {
                int64_t i;
                int8_t b[8];
            } Pun;
    
            qInfo() << "union Puning:";
            Pun.i = 0x0102030405060708;
            string tmp;
            for_each(begin(Pun.b), end(Pun.b), [&](int8_t bv){
                tmp += to_string(bv) + " ";
            });
            qInfo() << tmp.data();
    
            qInfo() << "reinterpret_cast puning:";
            int64_t i = 0x0102030405060708;
            int64_t *pi = &i;
            int8_t* pb;
            pb = reinterpret_cast<int8_t*>(pi);
            tmp.clear();
            for_each(pb, pb+8, [&](int8_t bv){
                tmp += to_string(bv) + " ";
            });
            qInfo() << tmp.data();
    
            qInfo() << "memcpy puning:";
            int8_t ab[8];
            memcpy(ab, &i, sizeof(ab));
            tmp.clear();
            for_each(begin(ab), end(ab), [&](int8_t bv){
                tmp += to_string(bv) + " ";
            });
            qInfo() << tmp.data();
        }
    

    So I just am back where I started. As long as the compiler supports this gcc/mingw I am just not inclined to change. Yes, I read the lengthy discussions on SO and elsewhere. I just don't see why this cannot be part of standard C++. Are the compiler makers just rebelling against the standard? I have C++17 turned on BTW.


  • Moderators

    @fcarney said in Puning... Back where I started with unions:

    Yes, I read the lengthy discussions on SO and elsewhere. I just don't see why this cannot be part of standard C++.

    It simply isn't, it's stated as undefined behaviour (in C++11); however all the compilers I've worked with just comply with the C99 standard on that particular topic (which states it's valid to read and write different fields of a union).


  • Lifetime Qt Champion

    @fcarney just to mention the obvious: memcpy on uint32_t and probably uint64_t ist most likely only load and store instructions, so the overhead is minimal. Esp. on I/O bound data transfer like CAN bus.

    Btw., do you use QtCanBus for that?

    Regards



  • I think we use some custom library for canbus. I am not sure where it is from. I was not involved in developing that piece.



  • Old thread and I should know better than to chime in, but everyone is entitled to my opinion. Also, I had this very issue posed to me the other day in our embedded domain.

    If you are dealing with existing CAN devices that expect network transport of IEEE floating point numbers then you have to go with what is expected and my comments are moot. However, if you have control over the endpoints in your networking of devices then you should NOT transport floating point numbers as such across a network. AUTOSAR, MISRA, and JPL coding standards all recognize why this is a bad idea.

    Our engineer is designing a microcontroller based controller and he did the union/float thing to represent data being exchanged. I quickly got him on-board to use scaled integers instead, which are more the standard in the automotive CAN arena. There are some legacy devices that use fixed precision ASCII representations of floating point numbers, such as NMEA/GPS, so that is also an option where bandwidth is not a concern.

    Some points to consider regarding transport of native floating point numbers:

    • different or adhoc endian format of devices
    • devices that require native types to start on processor word sized boundaries
    • devices that emulated floating point operations (no FPU)

    Anyway, just something to think about.


Log in to reply