为什么使用C语言联合体会得到这个输出?

3
#include<stdio.h>

union U{
    struct{
        int x;
        int y;
    };
    float xy;
};

int main(){
    union U u;
    u.x = 99;
    printf("xy %f\n",u.xy); //output " 0 "
    return 0;
}

我已经发现这与内部如何存储和读取浮点数有关。是否有人可以准确地向我解释一下?


3
使用%f进行打印并不是特别有用;你应该考虑使用%g%e。如果值非常小,即使它不为零,它也会打印为0.000000。(例如,任何比0.0000005更小的值都将打印为0.000000)。您需要阅读 IEEE 754 的维基百科页面,以了解这些值如何表示。例如,在运行macOS Sierra 10.12.5和GCC 7.2.0的Mac上,使用printf("xy %22.16g\n", u.xy);将生成xy 1.387285479681569e-43 - Jonathan Leffler
@JonathanLeffler; OP已经初始化了联合体u中未命名结构体的成员x。然后他使用另一个联合体成员xy来读取内容。有可能会出现陷阱表示,这种情况下代码的行为将是未定义的。 - haccks
4字节的float类型中正常数值的范围通常为10⁺³⁸到10⁻³⁸,因此float类型中的1.387…E-43是一个次标准值(尽管在8字节的double类型值的范围内)。 - Jonathan Leffler
@chiru 你没有初始化变量y,所以它可以有任何值。 - MCG
1
@MCG 变量 y 在此代码中未被访问,如果 sizeof(float) <= sizeof(int) - M.M
据我所知,这违反了严格别名规则,因此编译器可以假设.xy未初始化,并且对.x的写入是无效的。因此,它可能会将代码编译为int main(){ printf(“xy%f \ n”,0.0); },完全优化联合的使用。 我相信,将int的位重新解释为float的唯一安全方法是使用memcpy()int x = 99; float xy; memcpy(&xy,&x,sizeof(float)); - cmaster - reinstate monica
4个回答

1

将评论转换为答案。

使用%f打印不是很有用;您应该考虑使用%g%e。使用%f时,如果值非常小,则即使它不为零,也会被打印为0.000000。(例如,任何小于0.0000005的值都将被打印为0.000000。)您需要阅读维基百科上关于IEEE 754的内容,以了解这些值是如何表示的。

例如,在运行macOS Sierra 10.12.5的Mac上使用GCC 7.2.0进行打印:

printf("xy %22.16g\n", u.xy);

产生:

 xy 1.387285479681569e-43

4字节的float类型中正常数值范围通常为10⁺³⁸到10⁻³⁸,因此float类型的值1.387…E-43是次标准值(尽管在8字节的double类型值的范围内)。请记住,由于“默认参数提升”,传递给printf()float类型值会自动提升为double类型值,因此printf()实际上永远不会接收到float类型的值。

0

浮点数的表示方式会影响结果。请参考C语言中如何在内存中表示浮点数https://softwareengineering.stackexchange.com/questions/215065/can-anyone-explain-representation-of-float-in-memory来了解浮点数在内存中的表示方式。您还没有初始化结构体变量y,它可能具有任何值,可能被使用也可能不被使用。在您的情况下,该值非常小,您未打印完整值。为了查看float xy的值,您需要打印完整值。正如在这里的评论中建议的那样,在Windows(包含MinGW编译器)上使用Codeblocks 16.1中的以下语句,我得到的值与0.000000不同。

printf("xy %.80f\n",u.xy);

给我 xy 0.00000000000000000000000000000000000000000013872854796815689002144922874570169700


0

是的,你说得对,这与浮点值的二进制表示有关,该表示在标准文档IEEE 754中定义。请参阅Steve Hollasch的这篇优秀文章以获得简单的解释。浮点数是32位值,整数也是如此。因此,在您的联合U中,xy恰好落在嵌入式结构的x成员上,因此当您将x设置为99时,1100011(99的二进制表示)的位将被重新解释为float的尾数。正如其他人指出的那样,这是一个非常小的值,具体取决于printf格式说明符,可能会打印为0。

从您的联合成员的命名(x,y,xy)猜测,我认为您想声明不同的内容,例如:

union U
    {
    struct
        {
        short x;
        short y;
        };
    float xy;
    };

或者:

union U
    {
    struct
        {
        int x;
        int y;
        };
    double xy;
    };

在这些声明中,x和y成员都映射到xy成员。

-1

原因是该值非常接近于零,所以默认的6位精度无法显示任何内容。

尝试:

union { int i; float f; } u = {.i= 99};

printf("f %g\n", u.f);

1
也许您应该展示您得到了什么,并建议在哪里查找更多信息。 - Jonathan Leffler
或者 printf("xy %.80f\n",u.xy); - Support Ukraine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接