Details
-
Bug
-
Resolution: Done
-
P2: Important
-
4.7.0, 4.7.1, 4.7.2, 4.7.3, 4.7.4, 4.8.0, 4.8.1
-
None
-
Windows 7 32-bit
-
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.