在Raspberry Pi上的armv6中使用log10数学函数导致错误的结果

时间:2012-12-20 08:45:13

标签: c arm raspberry-pi

我有这个非常简单的代码:

#include <stdio.h>
#include <math.h>
int main()
{
    long v = 35;
    double app = (double)v;
    app /= 100;
    app = log10(app);
    printf("Calculated log10 %lf\n", app);
    return 0;
}

此代码完全适用于x86,但不适用于arm,结果为0.00000。有些想法?

其他信息:

操作系统:linux 3.2.27

我使用ct-ng构建arm工具链:arm-unknown-linux-gnueabi -

libc版本2.13

gcc -v的输出:

使用内置规格。 COLLECT_GCC =臂未知-Linux的gnueabi-GCC COLLECT_LTO_WRAPPER = /选择/ X-工具/臂未知-Linux的gnueabi /的libexec / GCC /臂未知-Linux的gnueabi / 4.5.1 / LTO-包装 目标:arm-unknown-linux-gnueabi 配置为:/home/mirko/misc/rasppi-ct-ng-files/.build/src/gcc-4.5.1/configure --build = x86_64-build_unknown-linux-gnu --host = x86_64-build_unknown-linux -gnu --target = arm-unknown-linux-gnueabi --prefix = / opt / x-tools / arm-unknown-linux-gnueabi --with-sysroot = / opt / x-tools / arm-unknown-linux- gnueabi / arm-unknown-linux-gnueabi // sys-root --enable-languages = c --disable-multilib --with-pkgversion = crosstool-NG-1.9.3 --enable -__ cxa_atexit --disable-libmudflap - -disable-libgomp --disable-libssp --with-host-libstdcxx =' - static-libgcc -Wl,-Bstatic,-lstdc ++, - Bdynamic -lm' - with-gmp = / home / mirko / misc / rasppi -ct-ng-files / .build / arm-unknown-linux-gnueabi / build / static --with-mpfr = / home / mirko / misc / rasppi-ct-ng-files / .build / arm-unknown-linux -gnueabi / build / static --with-mpc = / home / mirko / misc / rasppi-ct-ng-files / .build / arm-unknown-linux-gnueabi / build / static --with-ppl = / home / mirko / misc / rasppi-ct-ng-files / .build / arm-unknown-linux-gnueabi / build / static --with-cloog = / home / mirko / misc / rasppi-ct-ng-files / .build /臂UNK nown-linux-gnueabi / build / static --with-libelf = / home / mirko / misc / rasppi-ct-ng-files / .build / arm-unknown-linux-gnueabi / build / static --enable-threads = posix --enable-target-optspace --with-local-prefix = / opt / x-tools / arm-unknown-linux-gnueabi / arm-unknown-linux-gnueabi // sys-root --disable-nls - enable-symvers = gnu --enable-c99 --enable-long-long 线程模型:posix gcc版本4.5.1(crosstool-NG-1.9.3)

1 个答案:

答案 0 :(得分:10)

ARM Linux发行版上的浮点支持并非易事。因此,您应该使用与您的操作系统和系统匹配的工具链。硬件并使用正确的编译开关。

首先你需要了解ARM的调用约定,即关于“调用函数时如何传递参数?”。 ARM是RISC架构,只能在寄存器上工作。没有指令直接操作内存。如果你需要更改内存中的值,首先需要将其加载到寄存器中,修改它,然后你需要将它存储回内存。

当你调用一个函数时,你可能需要向它传递参数,你可以将参数放在堆栈(内存)上,但由于ARM只能使用寄存器,你的函数可能会做的第一件事就是将它们加载回寄存器。为了避免这种浪费,ARM调用约定使用寄存器来传递参数。但是由于ARM的寄存器数量有限,因此调用约定也要求您只使用前四个(r0-r3)寄存器作为前四个参数,剩下的仍然必须使用堆栈来传递。

第二件事是早期的ARM内核没有任何浮点支持,操作在软件中实现。 (这仍然是通过gcc的-mfloat-abi=soft支持的。)

我们可以通过以下代码段轻松演示这意味着什么。

float pi2(float a) {
    return a * 3.14f;
}

通过-c -O3 -mfloat-abi=softobdump来编译此内容

00000000 <pi2>:
   0:   f24f 51c3   movw    r1, #62915  ; 0xf5c3
   4:   b508        push    {r3, lr}
   6:   f2c4 0148   movt    r1, #16456  ; 0x4048
   a:   f7ff fffe   bl  0 <__aeabi_fmul>
   e:   bd08        pop {r3, pc}

正如您所看到的(实际上它不可见:))pi2r0中获取其参数,在pi constant上填充r1并使用__aeabi_fmul乘以这些并返回r0的结果。由于__aeabi_fmul也使用相同的调用约定,因此有关r0的详细信息不可见。我们所有的功能都填充r1并将其委托给__aeabi_fmul

当浮动硬件支持添加到ARM时(再次因为体系结构样式),它带有自己的一组寄存器(s0,s1,...)

如果我们使用-c -O3 -mfloat-abi=softfp编译相同的代码段并转储我们

00000000 <pi2>:
   0:   eddf 7a04   vldr    s15, [pc, #16]  ; 14 <pi2+0x14>
   4:   ee07 0a10   vmov    s14, r0
   8:   ee27 7a27   vmul.f32    s14, s14, s15
   c:   ee17 0a10   vmov    r0, s14
  10:   4770        bx  lr
  12:   bf00        nop
  14:   4048f5c3    .word   0x4048f5c3

正如您所看到的,编译器不会创建对__aeabi_fmul的调用,而是在将vmul.f32中的参数移动到r0之后创建s14指令。在3.14上填充s15。在乘法指令之后,它将s14中可用的结果移回r0 ,因为此函数的任何调用者都会因为调用约定而期望它

现在,如果您认为pi2是某个第三方提供给您的库,您可以理解soft和softfp实现对您做同样的事情,您可以互换使用它们。如果系统为您提供它们,您不关心您的应用程序是否在具有硬件浮点支持的系统上运行。保持旧软件在新硬件上运行非常好。

然而,在保持兼容性的同时,这种方法引入了在ARM寄存器和FP寄存器之间移动值的开销。这显然会影响性能并通过hard称为gcc的新调用约定来解决。这个新约定规定,如果函数中有浮点参数,则可以使用与正常值交错的浮点寄存器,也可以在浮点寄存器s0中返回浮点值。

再次,如果我们使用-c -O3 -mfloat-abi=hard编译我们的代码段并转储我们

00000000 <pi2>:
   0:   eddf 7a02   vldr    s15, [pc, #8]   ; c <pi2+0xc>
   4:   ee20 0a27   vmul.f32    s0, s0, s15
   8:   4770        bx  lr
   a:   bf00        nop
   c:   4048f5c3    .word   0x4048f5c3

你可以看到没有寄存器被移动。 pi2传递s0的参数,编译器创建的代码填充3.14中的s15并使用vmul.f32 s0, s0, s15获取我们想要的结果s0

这个新约定的大问题是当您改进编译器生成的代码时,您会彻底破坏兼容性。您不能指望使用hard约定构建的应用程序可以使用为soft/softfp构建的库,而为softfp构建的应用程序将不适用于为硬构建的库。

有关调用约定的更多信息,请检查ARM's website