Wrong conversion from double to QString in Qt on ARM
arm, c++, qt
Solution
This looks to be a sort of endianess issue, but not your plain-vanilla big-endian vs little-endian problem. ARM sometimes uses an unusual byte ordering for `double`. From "Handbook of Floating-Point Arithmetic" by Jean-Michel Muller, et al.:
... the double-precision number that is closest to `-7.0868766365730135 x 10^-268` is encoded by the sequence of bytes `11 22 33 44 55 66 77 88` in memory (from the lowest to the highest one) on x86 and Linux/IA-64 platforms (they are said to be little-endian) and by `88 77 66 55 44 33 22 11` on most PowerPC platforms (they are said to be big-endian). Some architectures, such as IA-64, ARM, and PowerPC are said to be bi-endian. i.e., they may be either little-endian or big-endian depending on their configuration.
There exists an exception: some ARM-based platforms. ARM processors have traditionally used the floating-point accelerator (FPA) architecture, where the double-precision numbers are decomposed into two 32-bit words in the big-endian order and stored according to the endianess of the machine, i.e., little-endian in general, which means that the above number is encoded by the sequence `55 66 77 88 11 22 33 44`. ARM has recently introduced a new architecture for floating-point arithmetic: vector floating-point (VFP), where the words are stored in the processor's native byte order.
When looked at in a big-endian byte order, `M_PI` will have a representation that looks like:
0x400921fb54442d18
The large number approximated by `8.6192e+97` wil have a representation that looks like:
0x54442d18400921fb
If you look closely, the two 32-bit words are swapped, but the byte order within the 32-bit words is the same. So apparently, the ARM 'traditional' double point format seems to be confusing the Qt library (or the Qt library is misconfigured).
I'm not sure if the processor is using the traditional format and Qt expects it to be in VFP format, or if things are the other way around. But it seems to be one of those two situations.
I'm also not sure exactly how to fix the problem - I'd guess there's some option for building Qt to handle this correctly.
the following snippet will at least tell you what format for `double` the compiler is using, which may help you narrow down what needs to change in Qt:
unsigned char* b;
unsigned char* e;
double x = -7.0868766365730135e-268;
b = (unsigned char*) &x;
e = b + sizeof(x);
for (; b != e; ++b) {
printf( "%02x ", *b);
}
puts("");
A plain little-endian machine will display:
11 22 33 44 55 66 77 88
Update with a bit more analysis:
At the moment, I'm unable to perform any real debugging of this (I don't even have access to my workstation at the moment), but by looking at the Qt source available on http://qt.gitorious.org here's additional analysis:
It looks like Qt calls in to the `QLocalePrivate::doubleToString()` function in qlocale.cpp to convert a `double` to an alphanumeric form.
If Qt is compiled with `QT_QLOCALE_USES_FCVT` defined, then `QLocalePrivate::doubleToString()` will use the platform's `fcvt()` function to perform the conversion. If `QT_QLOCALE_USES_FCVT` is not defined, then `QLocalePrivate::doubleToString()` ends up calling `_qdtoa()` to perform the conversion. That function examines the various fields of the `double` directly and appears to assume that the `double` is in a strict big-endian or little-endian form (for example, using the `getWord0()` and `getWord1()` functions to get the low and high word of the `double` respectively).
See http://qt.gitorious.org/qt/qt/blobs/HEAD/src/corelib/tools/qlocale.cpp and http://qt.gitorious.org/qt/qt/blobs/HEAD/src/corelib/tools/qlocale_tools.cpp or your own copy of the files for details.
Assuming that your platform is using the traditional ARM FPA representation for `double` (where the 32-bit halves of the `double` are stored in big-endian order regardless of whether the overall system being little-endian), I think you'll need to build Qt with the `QT_QLOCALE_USES_FCVT` defined. I believe that all you'll need to do is pass the `-DQT_QLOCALE_USES_FCVT` option to the configure script when building Qt.
Problem
I have Qt 4.4.3 built for ARMv5TE. I try to convert a `double` to a `QString`: ``` #include <QtCore/QtCore> #include <cmath> int main(int argc, char** argv) { const double pi = M_PI; qDebug() << "Pi is : " << pi << "\n but pi is : " << QString::number(pi, 'f', 6); printf("printf: %f\n",pi); return 0; } ``` but get strange output: ``` Pi is : 8.6192e+97 but pi is : "86191995128153827662389718947289094511677209256133209964237318700300913082475855805240843511529472.0000000000000000" printf: 3.141593 ``` How do I get the proper string?