【问题标题】:Is IEEE 754 format when using soft, softfp, hard floating point the same?使用soft、softfp、硬浮点时的IEEE 754格式是否相同?
【发布时间】:2018-03-02 13:41:52
【问题描述】:

TL;DR:0x400921f4 是 3.1415801 的 IEEE 754 表示形式吗?这是某种软浮动吗?我错过了什么?

我目前正在为 Raspberry Pi 3 和 Raspberry Pi Zero W 构建自己的交叉编译器时了解 crosstool-NG。(*1)

在阅读了分布在他们的论坛和 wiki 上的文档后,我了解到 RPi3 和 RPi0 编译器需要分别针对不同的 ARM 架构,ARMv7 和 ARMv6。但是,两个 SoC 都有一个浮点单元,所以我想使用硬浮点来匹配其余的 Raspberry Pi 库。

我针对RaspberryPi Tool repository 提供的 arm-linux-gnueabihf-gcc 4.9.3 测试了我新建的 armv6-rpi-linux-gnueabi-gcc 6.4.0。我阅读了如何确定可执行文件是否使用硬 fp here。果然,当我编译以下最小测试用例时。

我使用gcc -O0 -o main main.c 与两个编译器一起编译。在以下内容中:main.rpi 用于我的工具链,main.hf 用于预构建的工具链。

#include <stdio.h>

int main(int argc, char** argv) {
        printf("Hello, world %0.7f", 3.14158f);
        return 0;
}

readelf -A main.rpi 在使用我新建的 armv6-rpi-linux-gnueabi-gcc 时不会返回 Tag_ABI_VFP_args: VFP registers,但在使用预构建的 arm-linux-gnueabihf-gcc 时会返回 readelf -A main.hf。但是,这两个可执行文件在默认的 raspbian 拉伸图像上运行得很好,据说该图像仅支持硬 fp,输出如下:

Hello, world 3.1415801

奇怪的是,RPi 本身上的ldd main.rpi 只返回not a dynamic executable,而它列出了ldd main.hf 的依赖关系。同样,两者都运行并给出预期的输出。 objdump -R(在 RPi 上运行)对两个可执行文件返回相同的结果:

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
0002101c R_ARM_GLOB_DAT    __gmon_start__
0002100c R_ARM_JUMP_SLOT   printf@GLIBC_2.4
00021010 R_ARM_JUMP_SLOT   __libc_start_main@GLIBC_2.4
00021014 R_ARM_JUMP_SLOT   __gmon_start__
00021018 R_ARM_JUMP_SLOT   abort@GLIBC_2.4

这让我担心我使用了softsoftfp 而不是hard 浮点数。因此,我使用objdump -d(在 RPi 上运行)查看了两个可执行文件的反汇编列表:

main.rpi:
---------
0001045c <main>:
   1045c:       e92d4800        push    {fp, lr}
   10460:       e28db004        add     fp, sp, #4
   10464:       e24dd008        sub     sp, sp, #8
   10468:       e50b0008        str     r0, [fp, #-8]
   1046c:       e50b100c        str     r1, [fp, #-12]
   10470:       e3a02103        mov     r2, #-1073741824        ; 0xc0000000
   10474:       e59f3014        ldr     r3, [pc, #20]   ; 10490 <main+0x34>
   10478:       e59f0014        ldr     r0, [pc, #20]   ; 10494 <main+0x38>
   1047c:       ebffffa1        bl      10308 <printf@plt>
   10480:       e3a03000        mov     r3, #0
   10484:       e1a00003        mov     r0, r3
   10488:       e24bd004        sub     sp, fp, #4
   1048c:       e8bd8800        pop     {fp, pc}
   10490:       400921f4        .word   0x400921f4 <--- float 3.1415801 here
   10494:       00010508        .word   0x00010508 <--- pointer to format string

.

main.hf:
--------
000103f8 <main>:
   103f8:       e92d4800        push    {fp, lr}
   103fc:       e28db004        add     fp, sp, #4
   10400:       e24dd008        sub     sp, sp, #8
   10404:       e50b0008        str     r0, [fp, #-8]
   10408:       e50b100c        str     r1, [fp, #-12]
   1040c:       e59f0018        ldr     r0, [pc, #24]   ; 1042c <main+0x34>
   10410:       e3a02103        mov     r2, #-1073741824        ; 0xc0000000
   10414:       e59f3014        ldr     r3, [pc, #20]   ; 10430 <main+0x38>
   10418:       ebffffa1        bl      102a4 <printf@plt>
   1041c:       e3a03000        mov     r3, #0
   10420:       e1a00003        mov     r0, r3
   10424:       e24bd004        sub     sp, fp, #4
   10428:       e8bd8800        pop     {fp, pc}
   1042c:       000104a4        .word   0x000104a4 <--- pointer to format string
   10430:       400921f4        .word   0x400921f4 <--- float 3.1415801 here

令我大吃一惊的是,两个编译器都为 main 生成了相同的结果,差异很小。 (*2) 在我看来,两者都像 hardfp。

我检查了是否有一些诡计,编译器将浮点数放在实际的字符串中,但没有发现。然后我随便确保 0x400921f4 确实是我打印的浮点值 3.1415801。我使用两个在线工具进行了检查,herehere。那是我对浮点数据的理解明显崩溃的时候。

所有转换器都告诉我 3.1415801 应该是 0x40490fa6 并且两个可执行文件中的文字 0x400921f4 产生 ~2.1426973。两个编译器是否都使用特殊的(软件?)浮点格式,即使它们表明它们使用Tag_ABI_FP_number_model: IEEE 754?我一直认为软件浮点只是常规的 IEEE 754,但在软件中计算而不是不同的格式。

如果我成功使用 hardfp 并且我的推理不正确,或者当我什至在反汇编中看不到正确的浮点常量时如何解释明显正确的行为,我现在真的很困惑。


(*1) 因为这是我第一次使用 crosstool-NG,所以我使用了他们的示例配置 armv6-rpi-linux-gnueabi,但将 gcc 降级到 6.4.0 和 gdb 降级到 7.12.1 以大致匹配可用的交叉编译器工具链对于 Windows。我还针对内核 4.4.83 及更高版本而不是 3.12.74。

(*2) 我检查了 printf@plt 中是否隐藏了软浮点操作,但事实并非如此。两者的程序集是(具有不同的地址):

00010308 <printf@plt>:
   10308:       e28fc600        add     ip, pc, #0, 12
   1030c:       e28cca10        add     ip, ip, #16, 20 ; 0x10000
   10310:       e5bcfcfc        ldr     pc, [ip, #3324]!        ; 0xcfc

【问题讨论】:

  • 虽然没有必要这样做,但我还没有看到编译器在硬浮点和软浮点之间来回切换时使用不同的格式。但也许那些编译器就在那里。
  • 嗯...尽管有答案,但 ISTM 认为 0x400921F4 是形成 8 字节双精度 3.1415801 的字节的十六进制表示的部分(完整的十六进制值:0x400921F4BF5A1D83)。 3.1415801f 的 4 字节浮点值是 0x40490FA6

标签: floating-point raspberry-pi arm


【解决方案1】:

当浮点数被传递给 printf() 时,它被提升为双精度,并且编译器已经优化了转换。 (double)(3.14158f) 的表示是0x400921f4c0000000。 MS部分进入r3,LS部分进入r2。

此外,由于 printf() 是可变参数,因此在两种情况下都使用整数寄存器传递 FP 值(软浮点样式)。

【讨论】:

  • 哇!感谢您及时的回复。我想我昨天晚上很密集。完全有道理。我会等一天左右然后接受!
【解决方案2】:
float  fun ( float a )
{
    return(a+1.234F);
}

硬浮动

00000000 <fun>:
   0:   eddf 7a02   vldr    s15, [pc, #8]   ; c <fun+0xc>
   4:   ee30 0a27   vadd.f32    s0, s0, s15
   8:   4770        bx  lr
   a:   bf00        nop
   c:   3f9df3b6    svccc   0x009df3b6

软浮动

00000000 <fun>:
   0:   b508        push    {r3, lr}
   2:   f24f 31b6   movw    r1, #62390  ; 0xf3b6
   6:   f6c3 719d   movt    r1, #16285  ; 0x3f9d
   a:   f7ff fffe   bl  0 <__aeabi_fadd>
   e:   bd08        pop {r3, pc}

相同的常数是正确的常数。

但正如在我得到我的答案之前出现的答案中所指出的那样。除非另有说明,否则 C 中的浮点数是双精度的,请注意我的常量末尾的 F,而编译器必须将其提升为双精度才能执行操作(因为这是我通过不放置 F 来告诉它的,常量否则被视为双)然后转换回单返回。

00000000 <fun>:
   0:   b508        push    {r3, lr}
   2:   f7ff fffe   bl  0 <__aeabi_f2d>
   6:   a304        add r3, pc, #16 ; (adr r3, 18 <fun+0x18>)
   8:   e9d3 2300   ldrd    r2, r3, [r3]
   c:   f7ff fffe   bl  0 <__aeabi_dadd>
  10:   f7ff fffe   bl  0 <__aeabi_d2f>
  14:   bd08        pop {r3, pc}
  16:   bf00        nop
  18:   c8b43958    ldmgt   r4!, {r3, r4, r6, r8, r11, r12, sp}
  1c:   3ff3be76    svccc   0x00f3be76

【讨论】:

    猜你喜欢
    • 2014-07-17
    • 1970-01-01
    • 2012-01-06
    • 2014-10-18
    • 1970-01-01
    • 1970-01-01
    • 2013-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多