嵌入式平台上一个浮点处理的问题
时间:2007-03-05 来源:rockins
嵌入式平台上一个浮点处理的问题
(陈云川 [email protected] UESTC,CD)
1 引言
本文作者在为Sitsang评估板编写一个从二进制文件中读取浮点数的程序时,发现一个奇怪的问题:在x86上编译后能够正确运行的程序,交叉编译后拿到评估板上跑就不对了。比如,本来文件中保存的二进制数据的值应该是6244.760200f,在PC上读出来的值也是对的,但是在评估板上运行时读出来的值就是0.00000f。我首先当然怀疑是字节序的问题,于是写了一个小程序测试了一下PC机和Sitsang评估板的整数的字节序,发现两者的字节序都是小端字节序。我一再地仔细检查了程序逻辑,发现程序在逻辑上应该是没有问题的,于是问题的焦点就落到了Sitsang评估板如何表示浮点数据上。同样地,我写了一个小测试程序,用来测试这一疑点。测试的结果有点出乎意料,一个占据8字节的双精度浮点数,在x86平台上和在Sitsang评估板上的表示是不同的。不同之处在于:在Sitsang评估板上,双精度浮点数的前4个字节和后4个字节恰好与在x86平台上的前4个字节和后4个字节是相反的。而这一点直接导致了问题的发生——因为采用的二进制数据文件是在x86平台上制作完成的。
2 测试程序
测试程序非常简单,如下所示:
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
double d = 6244.760200f;
unsigned char *p;
int i;
p = (char *)&d;
printf("%f:", d);
for (i = 0; i < sizeof(d); i++) {
printf("%02x ", p[i]);
}
printf("\n");
return (0);
}
3 测试结果
要在x86平台上运行,只需像下面这样编译并运行即可:
[root@cyc test]# gcc -o test_double test_double.c
[root@cyc test]# ./test_double
6244.760254:00 00 00 a0 c2 64 b8 40
而要在Sitsang评估板上运行测试程序,只需像下面这样编译程序:
[root@cyc test]# arm-linux-gcc -o test_double test_double.c
然后,再通过NFS或者FTP等途径将编译后的可执行程序test_double下载到Sitsang评估板上,如下执行之[1]:
[root@Sitsang2 TinyGIS]$chmod +x test_double
[root@Sitsang2 TinyGIS]$./test_double
6244.760254:c2 64 b8 40 00 00 00 a0
为了便于对比,不妨以表格的形式将这两种平台的表示方式列出来,如表 1所示。
表 1 x86和XScale对浮点数表示的对比
double |
6244.760200f |
X86(PC) |
00 00 00 a0 c2 64 b8 40 |
XScale(Sitsang) |
c2 64 b8 40 00 00 00 a0 |
从表 1可知:浮点数在两种平台上的表示方式的确存在差异,因此,为了能够在Sistang评估板上正确读出数据文件中的浮点数值,应该在读出之后将浮点数的前4字节与后4字节交换。
4 对策
用一个宏即可解决浮点数表示不同的问题。以下就是作者所编写的宏,可能效率不是太高,但是比较容易理解,姑且采用之。
// for double float on PXA255, I found that its
// first 4 bytes and second 4 bytes should swaped
// to act as it does in X86 PC platform, so I define
// this macro, when work on X86 PC, this macro
// is defined as empty
#if defined(PXA_XSCALE)
#define SWAP_DOUBLE(x) \
do { \
unsigned int *p, t; \
p = (unsigned int *)&x; \
t = p[0]; \
p[0] = p[1]; \
p[1] = t; \
}while(0)
#elif defined(X86_PC)
#define SWAP_DOUBLE(x)
#endif
宏定义SWAP_DOUBLE()是程序中真正需要的,对于PC平台,不需要交换浮点数的前后4个字节,因此将SWAP_DOUBLE()定义为空。
测试表明,经过这样的处理之后,得到的结果就是正确的了。
5 猜想
尽管问题已经被解决了,但是其根源作者并未搞清楚。目前的猜测是这样的:由于在很多ARM之类的嵌入式平台上是没有浮点硬件的,因此,对浮点数的处理必须由编译器来完成。而上述浮点表示方式的差异应该是编译器引入的。但这仅仅属于猜想,真实的情况是否如此,尚未找到有力的证据支持。
[1] 输入的值是6244.760200f,但打印出的值是6244.760254f,最后两位的误差是由于计算机的浮点表示精度有限而导致的