【问题标题】:Understanding GCC's floating point constants in assembly listing output了解汇编列表输出中的 GCC 浮点常量
【发布时间】:2018-08-16 18:34:25
【问题描述】:

出于好奇,我使用Compiler Explorer 来查看一些简单C++ 代码的汇编输出。

考虑以下example

int main(void){
    double x = -5.3;
}

汇编输出

main:
        push    rbp
        mov     rbp, rsp
        movsd   xmm0, QWORD PTR .LC0[rip]
        movsd   QWORD PTR [rbp-8], xmm0
        mov     eax, 0
        pop     rbp
        ret
.LC0:
        .long   858993459
        .long   -1072352461

我想了解如何使用

.LC0:
        .long   858993459
        .long   -1072352461

找回我的-5.3

我不知情的猜测是我需要合并两个 32 位整数的位模式,并将其解释为双精度浮点数的位模式。但是,究竟如何?我必须将模式解释为 IEEE754 双精度吗?按什么顺序?

【问题讨论】:

  • 第一个long(32位)是64位双浮点数的下半部分,第二个是上半部分。您可以将这两个值转换为十六进制,然后将它们连接在一起以获得 64 位十六进制值。 64 位十六进制值将采用 IEEE-754 格式。在这种情况下,转换第一个值是 HEX 33333333 。第二个是C0153333。 64 位 HEX 值为 C015333333333333,应为 -5.3。
  • 您可以使用在线转换器将 64 位编码的 IEE754 值转换为十进制浮点数:babbage.cs.qc.cuny.edu/IEEE-754.old/64bit.html
  • 维基百科以及其他地方显示了 IEEE 754 浮点数的细分/格式,因此您可以查看这些位的位置/方式。更容易从单精度开始,然后逐步提高,但与此同时,随着您逐步提高,它只是更多的位。格式的工作方式相同。
  • 在 CE 中,您可以将鼠标悬停在常量上以查看十六进制值。在这种情况下,Clang 和 ICC 输出更具可读性,您应该尝试这些

标签: gcc assembly floating-point x86-64


【解决方案1】:

但是,究竟如何? ...

是的,这是IEEE754 binary64 (aka double) 位模式的整数表示。 GCC 总是以这种方式打印 FP 常量,因为它们有时是常量传播的结果,而不是出现在源代码中的 FP 文字。 (还避免了对汇编程序中 FP 舍入的任何依赖。)

gcc 在其 asm 输出中总是使用十进制表示整数常量,这对人类来说非常不方便。 (On the Godbolt compiler explorer,使用鼠标悬停工具提示获取任意数字的十六进制。)

Clang 的 asm 输出更好,并且包含带有数字十进制值的注释:

    .quad   -4605718748921121997    # double -5.2999999999999998

按什么顺序?

x86 的浮点字节序匹配其整数字节序:两者都是小字节序。 (情况可能并非如此,但所有现代主流架构都对整数和浮点数使用相同的字节顺序,无论大小。Floating point Endianness?。还有Endianness for floating point。)

所以当作为64-bit IEEE-754 double加载时,内存中的低32位就是double的低32位。

正如@MichaelPetch 在 cmets 中解释的那样,第一个/低位 dword 是 0x33333333,第二个/高位 dword 是 0xC0153333因此整个double 的位模式为C015333333333333

对于单精度浮点数,有https://www.h-schmidt.net/FloatConverter/IEEE754.html。 (非常棒,它使用复选框将位分解为二进制,以及十六进制位模式和十进制小数。非常适合了解 FP 指数/有效数字的工作原理。)

对于双精度,请参阅https://babbage.cs.qc.cuny.edu/IEEE-754.old/64bit.html。您可以输入位模式并查看十六进制值。

【讨论】:

  • 为什么它被转换为长,因为在汇编或其他东西中没有立即的浮点数?
  • @Srilakshmikanthan:编译器已经知道它想要什么位模式,因此将其作为 asm 中的整数发出会更容易、更有效。它还避免了对主机浮点格式与目标浮点格式的任何依赖。 double -> string 很慢, string->double 也很慢(对于必须解析此文件的汇编程序)。 GAS 确实支持浮点文字,但编译器生成的代码选择不使用它们。 (您说“立即”:这是机器指令的文字操作数。在这种情况下,它只是 .quad.long 的文字数据)
  • 我不明白的是,没有浮点立即汇编,所以浮点数在全局(stackoverflow.com/questions/47946389/…)中声明。如果在 c 中使用整数转换为 asm,它不包含 int 的全局变量但是对于 float 全局变量是存在的,所以我假设对于所有浮点变量都有一个全局变量,因为没有像 (movsd xmm0,12.12) 这样的浮点立即数,对吗?
  • @Srilakshmikanthan:哦,是的,没错。 x86 没有立即源 FP 指令。 (有趣的事实:我认为 ARM 有一些)。一些汇编器的语法让您可以使用mov rax, 3.14 将双精度位模式输入 RAX,因此您可以使用movq xmm0, rax。当然,对于任何汇编程序,您都可以将整数位模式用于mov rax, 0xC015333333333333。但这通常不如从 .rodata 加载浮点数/双精度数。
  • 非常感谢,我对这一行中的另一件事有疑问movsd xmm0, QWORD PTR .LC0[rip] rip.LC0 之间的关系是什么我看到了一些关于此的问题,但我不明白为什么不是这样movsd xmm0, QWORD PTR [.LC0] 是错的?
【解决方案2】:
#include <iostream>
typedef struct{
    union{
        double decimal;
        struct{
            int a;
            int b;
        }v;
    };
}Double2Int_t;
int main(){
    int a1=858993459;
    int a2=-1072352461;
    double value=-5.3;
    Double2Int_t decimal;
    decimal.decimal=value;
    std::cout<<decimal.v.a<<" "
            <<decimal.v.b<<std::endl;
    Double2Int_t decimal2;
    decimal2.v.a=a1;
    decimal2.v.b=a2;
    std::cout<<decimal2.decimal<<std::endl;
    return 0;
}

【讨论】:

    猜你喜欢
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多