【问题标题】:Display bit pattern of double as hex?将双精度位模式显示为十六进制?
【发布时间】:2018-11-07 06:14:55
【问题描述】:

我正在尝试使用printf 将双精度的二进制表示打印到控制台上。这是我到目前为止的代码:

#include <stdio.h>

int main()
{
    double a = 4.75;
    unsigned long long b = *(unsigned long long *) &a;
    printf("Double a = %f (0x%x)" a, b);
}

我也尝试将它从 long long 改为 long。这是我在 gcc linux 中执行此操作得到的结果:

Double a = 4.750000 (0x0)

我很确定,因为我的系统是小端序,它只抓取全为零的前 32 位。但是,我不确定如何解决这个问题。

我认为这可能是因为 long 在 linux 中是 64 位的,但经过测试,我再次得到了相同的结果。也许我打印不正确?不确定,请告诉我,感谢您抽出宝贵时间。

【问题讨论】:

  • *(unsigned long long *) &amp;a; 不违反严格别名吗?
  • @SouravGhosh 是的,请参阅What is strict aliasing
  • printf("Double a = %f (0x%llx)", a, b);
  • @SouravGhosh 该信息适用于可能不理解的普通观众;-)
  • 我真的不明白这不是是对严格别名问题的欺骗。如果我们要保持开放,问题需要缩小到 C 或 C++。目前它太宽泛了。如果拥有金牌徽章的高代表用户在写答案之前努力缓和 C 和 C++ 标记的交叉发布方面,将会很有帮助。

标签: c++ c floating-point double type-punning


【解决方案1】:

您的代码有两个问题,一个简单的问题是您使用了错误的格式说明符%llxunsigned long long 的正确说明符。 clang 和 gcc 都使用 -Wallsee it live 对此提供警告。

第二个问题是你在这里违反了strict aliasing rule

unsigned long long b = *(unsigned long long *) &a;

键入双关的正确方法(在 C 和 C++ 中)是使用 memcpy (see it working live):

std::memcpy( &b, &a, sizeof(double));

在 C++20 中注释上面的严格别名链接中,我们应该得到 bit_cast,这将简化类型双关,例如:

b = bit_cast<unsigned long long>(a);

【讨论】:

    【解决方案2】:

    在 C 中,代码类似于

    unsigned long long b = *(unsigned long long *) &a;
    

    是非法的,因为它违反了关于一种类型的指针引用不同类型的对象的严格别名规则。

    但是,有一个例外:char 指针可以为任何对象起别名

    所以使用 char 指针,你可以在 C 中做这样的事情:

    #include <stdio.h>
    
    int main()
    {
        double a = 4.75;
        unsigned char* p;
    
        // Method 1 - direct memory read
        p = (unsigned char*)&a;
        for (size_t i=0; i < sizeof(double); ++i)
        {
            printf("%02x ", *p);
            ++p;
        }
        printf("\n");
    
        // Method 2 - reversed memory read
        size_t i = sizeof(double);
        p = (unsigned char*)&a + i - 1;
        do
        {
            printf("%02x ", *p);
            --p;
            --i;
        } while(i > 0);
    
        return 0;
    }
    

    输出:

    00 00 00 00 00 00 13 40 
    40 13 00 00 00 00 00 00
    

    这两种方法以不同的字节顺序打印。

    如果你不喜欢指针,你也可以使用联合。喜欢:

    #include <stdio.h>
    
    int main()
    {
        union
        {
            double a;
            char c[sizeof(double)];
        } d;
        d.a = 4.75;
    
        for (size_t i=0; i < sizeof(double); ++i)
        {
            printf("%02x ", d.c[i]);
        }
        printf("\n");
    
        size_t i = sizeof(double);
        do
        {
            --i;
            printf("%02x ", d.c[i]);
        }
        while (i > 0);
        printf("\n");
    
    
        return 0;
    }
    

    输出:

    00 00 00 00 00 00 13 40 
    40 13 00 00 00 00 00 00 
    

    【讨论】:

    • 一个小补充:类型双关语在 C11 中是标准的,但在 C++ 中是不允许的。您将您的答案献给了 C。希望 OP 确实注意到了这一点。
    【解决方案3】:

    在 C++ 中:

    double a = 4.75;
    char tmp[sizeof(double)];
    memcpy(tmp, &a, sizeof(double));
    ... // print individual bytes as hex
    

    或者:

    double a = 4.75;
    unsigned char* tmp = reinterpret_cast<unsigned char*>(&a);
    for (int i = 0; i < sizeof(double); i++)
      ... // print *(tmp + i) as hex
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-07
      • 2016-12-14
      • 2014-05-05
      • 2011-04-03
      相关资源
      最近更新 更多