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?
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 bytes11 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 by88 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.
The same code produces proper output on an x86 machine (running Windows XP) with Qt 4.7.0.
I see the following possibilities for the source of the problem:
I found this forum post on a similar problem which supposes it could be a big/little-endian conversion problem.
I can't tell how to fix this as I am not experienced with ARM at all but maybe this information helps you anyway.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With