Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-25950

Broken Code: QDataStream::operator<<(float f)

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P2: Important
    • Resolution: Done
    • Affects Version/s: 4.7.0, 4.7.1, 4.7.2, 4.7.3, 4.7.4, 4.8.0, 4.8.1
    • Fix Version/s: 4.8.5, 5.0.0 RC 1, 5.0.0
    • Component/s: Core: I/O
    • Labels:
      None
    • Environment:
      Windows 7 32-bit
    • Commits:
      Qt5: 3f936e9094f3a6e4d76791c1eff7ae92f91b61ae Qt4: e2f600de9553168b3eb4fcbd1d4c523a91ee33ef

      Description

      Code of QDataStream::operator<<(float f) was changed in Qt 4.7.0 and sometimes does incorrect job since. The latest QT version 8 still has that broken code.

      Here are the copy of code from versions 4.7.0 and 4.6.2:

      //Qt 4.6.2 - works correctly
      
      QDataStream &QDataStream::operator<<(float f)
      {
          if (version() >= QDataStream::Qt_4_6
              && floatingPointPrecision() == QDataStream::DoublePrecision) {
              *this << double(f);
              return *this;
          }
      
          CHECK_STREAM_PRECOND(*this)
          float g = f;                                // fixes float-on-stack problem
          if (noswap) {                                // no conversion needed
              dev->write((char *)&g, sizeof(float));
          } else {                                // swap bytes
              union {
                  float val1;
                  char val2[4];
              } x;
              x.val1 = f;
              char *p = x.val2;
              char b[4];
              b[3] = *p++;
              b[2] = *p++;
              b[1] = *p++;
              b[0] = *p;
              dev->write(b, 4);
          }
          return *this;
      }
      
      //Qt 4.7.0 - improved efficiency, but works incorrectly on special cases
      
      QDataStream &QDataStream::operator<<(float f)
      {
          if (version() >= QDataStream::Qt_4_6
              && floatingPointPrecision() == QDataStream::DoublePrecision) {
              *this << double(f);
              return *this;
          }
      
          CHECK_STREAM_PRECOND(*this)
          float g = f;                                // fixes float-on-stack problem
          if (!noswap) {
              union {
                  float val1;
                  quint32 val2;
              } x;
              x.val1 = g;
              x.val2 = qbswap(x.val2);
              g = x.val1;
          }
          dev->write((char *)&g, sizeof(float));
          return *this;
      }
      

      In order to swap bytes, QT copies float g to a float member of union x, swap bytes in the integer member of union, and then copy the float member back to variable g, which is finally written to the stream. All is good except, when float member of union x.val1 becomes meaningless as a float after swapping bytes. Then equal operator "g=x.val1" recovers float number consistency and may change some bits.

      Example:
      f = 8226.9990 approximately, binary image is 0x46008bff.

      Then:
      x.val1 = 8226.999 float
      x.val2 = 0x46008bff unsigned int

      After swapping bytes:
      x.val1 = -1.#QNAN00 float //as shown in VisualStudio 2010 debugger
      x.val2 = 0xff8b0046 unsigned int

      After g=x.val1, binary image of g is 0xffCb0046 instead of 0xff8b0046, which is one-bit different and after swapping bytes back, the float value become 8242.999 instead of 8226.999 expected.

      Regardless to this problem, extra variable g seems to be excessive either. For example, a correct and efficient version of this code would be:

      QDataStream &QDataStream::operator<<(float f)
      {
          if (version() >= QDataStream::Qt_4_6
              && floatingPointPrecision() == QDataStream::DoublePrecision) {
              *this << double(f);
              return *this;
          }
      
          CHECK_STREAM_PRECOND(*this)
          union {
             float val1;
             quint32 val2;
          } x;
          x.val1 = f;  // fixes float-on-stack problem
          if (!noswap) {
              x.val2 = qbswap(x.val2);
          }
          dev->write((char *)&x, sizeof(float));
          return *this;
      }
      

      I did not search, but it is very possible that similar logic is in use in some other operators, like operator<<(double f), for example, and it all needs to be fixed as well.

        Attachments

        1. qt-25950.zip
          0.6 kB
          Caroline Chao
        2. QTBUG-25910.zip
          13 kB
          Mitch Curtis
        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

            Assignee:
            stromme Christian
            Reporter:
            sr2020 Victor Yartsev
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:

                Gerrit Reviews

                There are no open Gerrit changes