Неверный результат с математической функцией log10 в armv6 на малине 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, , но не работает на плече, на котором результат равен 0,00000. Некоторые идеи?

Дополнительная информация:

Операционная система: linux 3.2.27

Я создаю инструмент toolchain с ct-ng: arm-unknown-linux-gnueabi -

libc version 2.13

Вывод gcc -v:

Использование встроенных спецификаций. COLLECT_GCC = рычажного неизвестно-линукс-gnueabi-НКУ COLLECT_LTO_WRAPPER =/опт/х-инструменты/арм-неизвестный-Linux-gnueabi/libexec/ССАГПЗ/рычажный неизвестно-линукс-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, -lstdС++, - 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

Поддержка плавающей точки в дистрибутивах 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=soft и obdump ing дает нам

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}

Как вы можете видеть (на самом деле это не видно:)) pi2 получает свой параметр в r0, заполняет 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.