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

Mir*_*chi 15 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;
}
Run Code Online (Sandbox Code Playgroud)

此代码在x86上完美运行,但在arm上不起作用,结果为0.00000.一些想法?

其他信息:

操作系统:linux 3.2.27

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

libc版本2.13

产量gcc -v:

使用内置规格.COLLECT_GCC = arm-unknown-linux-gnueabi-gcc COLLECT_LTO_WRAPPER =/opt/x-tools/arm-unknown-linux-gnueabi/libexec/gcc/arm-unknown-linux-gnueabi/4.5.1/lto-wrapper目标:手臂-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/arm-unknown-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)

aus*_*len 10

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

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

当你调用一个函数时,你可能需要向它传递参数,你可以将参数放在堆栈(内存)上,但由于ARM只能使用寄存器,所以你的函数可能会先将它们加载回寄存器.为了避免这种浪费,ARM调用约定使用寄存器来传递参数.但是,由于ARM具有有限数量的寄存器,因此调用约定还要求您仅对前四个参数使用前四个(r0-r3)寄存器,其余仍必须使用堆栈来传递.

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

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

float pi2(float a) {
    return a * 3.14f;
}
Run Code Online (Sandbox Code Playgroud)

编译这个via -c -O3 -mfloat-abi=softobdumping给了我们

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}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的(实际上它是不可见的:))pi2在得到它的参数r0,填充pi constantr1,并使用__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
Run Code Online (Sandbox Code Playgroud)

正如你可以看到现在编译器不建立一个呼叫__aeabi_fmul,而是它创造了vmul.f32它移动位于争吵后指令r0s14并填充3.14s15.在乘法指令之后,它会将结果移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
Run Code Online (Sandbox Code Playgroud)

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

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

有关调用约定的更多信息,请查看ARM的网站.