ARM上Qt中从double到QString的错误转换

时间:2012-04-13 13:57:02

标签: c++ qt arm

我为ARMv5TE构建了Qt 4.4.3。我尝试将double转换为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;
}  

但得到奇怪的输出:

Pi is :  8.6192e+97 
but pi is :  "86191995128153827662389718947289094511677209256133209964237318700300913082475855805240843511529472.0000000000000000"
printf: 3.141593

如何获得正确的字符串?

2 个答案:

答案 0 :(得分:8)

这看起来像是一种endianess问题,但不是普通的big-endian和little-endian问题。 ARM有时会为double使用不常见的字节顺序。来自Jean-Michel Muller等人的“浮点运算手册”:

  

...最接近的双精度数   -7.0868766365730135 x 10^-268由字节序列编码   记忆中11 22 33 44 55 66 77 88(从最低到最高   一)在x86和Linux / IA-64平台上(据说它们是    little-endian )和88 77 66 55 44 33 22 11在大多数PowerPC平台上(据说它们都是 big-endian )。一些架构,例如   因为IA-64,ARM和PowerPC被认为是双端。也就是说,他们可能   根据他们的不同,可以是小端或大端   配置。

     

存在一个例外:一些基于ARM的平台。 ARM处理器   传统上使用浮点加速器(FPA)   体系结构,其中双精度数被分解为   两个32位字的big-endian顺序并按照存储的方式存储   机器的端部,即一般的小端,这意味着   上述数字由序列55 66 77 88 11 22 33 44编码。 ARM最近推出了一种新的浮点架构   arithmetic:向量浮点(VFP),其中存储了单词   处理器的本机字节顺序。

以big-endian字节顺序查看时,M_PI将具有如下表示:

0x400921fb54442d18

8.6192e+97近似的大数字表示如下:

0x54442d18400921fb

如果仔细观察,会交换两个32位字,但32位字内的字节顺序是相同的。显然,ARM的“传统”双点格式似乎让Qt库(或者Qt库配置错误)感到困惑。

我不确定处理器是否使用传统格式,而且Qt期望它采用VFP格式,或者事情是相反的。但它似乎是这两种情况之一。

我也不确定如何解决问题 - 我猜有一些选项可以构建Qt来正确处理这个问题。

以下代码段至少会告诉您编译器正在使用的double格式,这可能会帮助您缩小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("");

将显示一个简单的小端机器:

11 22 33 44 55 66 77 88

通过更多分析进行更新:

目前,我无法对此进行任何实际调试(目前我甚至无法访问我的工作站),但通过查看http://qt.gitorious.org上可用的Qt源,此处还有分析:

看起来Qt调用qlocale.cpp中的QLocalePrivate::doubleToString()函数将double转换为字母数字形式。

如果Qt是在定义QT_QLOCALE_USES_FCVT的情况下编译的,那么QLocalePrivate::doubleToString()将使用平台的fcvt()函数来执行转换。如果<{1}} 定义,则QT_QLOCALE_USES_FCVT最终会调用QLocalePrivate::doubleToString()来执行转换。该函数直接检查_qdtoa()的各个字段,并假设double采用严格的big-endian或little-endian形式(例如,使用double和{ {1}}函数分别获取getWord0()的低位和高位字。

有关详细信息,请参阅http://qt.gitorious.org/qt/qt/blobs/HEAD/src/corelib/tools/qlocale.cpphttp://qt.gitorious.org/qt/qt/blobs/HEAD/src/corelib/tools/qlocale_tools.cpp或您自己的文件副本。

假设您的平台正在使用getWord1()的传统ARM FPA表示(double的32位一半以big-endian顺序存储,无论整个系统是否为小 - endian),我认为你需要用double定义来构建Qt。我相信你需要做的就是在构建Qt时将double选项传递给configure脚本。

答案 1 :(得分:4)

相同的代码使用Qt 4.7.0在x86计算机(运行Windows XP)上生成正确的输出。

我看到了问题根源的以下可能性:

  • 可能在较新版本的Qt
  • 中修复了一些错误
  • 编译ARM时出现问题

我发现了this forum post类似的问题,假设它可能是一个大/小端转换问题。

我不知道如何解决这个问题,因为我根本没有使用过ARM,但这些信息可能对你有所帮助。