为什么从 float 到 int 的转换会返回 float 的整数部分(在 C 中)?

Why does casting from float to int return the integer part of the float (in C)?

提问人:BamsBamx 提问时间:10/19/2023 最后编辑:BamsBamx 更新时间:10/20/2023 访问量:116

问:

众所周知,在将浮点数转换为 int 后,int 变量会获取浮点变量的整数部分,而丢弃小数部分。

以一个 32 位浮点数为例,其值为 -248.75,内存中的值如下:

float_t a = -248.75; // in binary: 11000011 01111000 11000000 00000000

如下图所示:

enter image description here

该二进制值以十进制表示以下值:

11000011 01111000 11000000 00000000 = 3279470592

然后,据我所知,将二进制内容转换为(无符号或无符号)整数应该返回以下值:

uint32_t u32 = (uint32_t) a;   -> 3,279,470,592  (**11000011 01111000 11000000 00000000**)
uint16_t u16 = (uint16_t) a;   -> 49152          (00000000 00000000 **11000000 00000000**)
uint8_t  u8  = (uint8_t)  a;   -> 0              (00000000 00000000 00000000 **00000000**)
int32_t  i32 = (int32_t)  a;   -> -1,131,986,944 (**01000011 01111000 11000000 00000000**) (the first bit is the sign)

那么,为什么这些变量会得到 248 值呢?(反之亦然,从整数到浮点数)PD:我问的是技术背景

C 转换 浮点 二进制

评论

6赞 JohnFilleau 10/19/2023
因为这就是从浮点型到整数类型的转换的定义方式。之所以这样定义,是因为它满足了真实的业务用例。它将继续以这种方式定义,因为更改它将破坏现有代码并不再满足真正的业务用例。
0赞 Simon Goater 10/19/2023
如果你想要二进制表示,你可以使用带有 uint32_t 的并集,或者只使用 memcpy 4 个字节。
0赞 cafce25 10/19/2023
您似乎想重新解释字节,而不是强制转换值,因为前者执行您描述的操作,而后者(嗯...)强制转换类型,该类型是按照您的体验方式定义的。
3赞 Weather Vane 10/19/2023
该示例显示,你希望看到用于编码的位模式,但无法通过强制转换来查看。一种方法是floatmemcpy(&u32, &f32, sizeof u32);
2赞 Support Ukraine 10/19/2023
强制转换是一种从一种类型转换为另一种类型的方法。强制转换不应保留二进制模式。有时会,有时不会。在浮点数和整数之间进行强制转换不会保留位模式(某些特殊值除外)。

答:

1赞 0___________ 10/19/2023 #1

因为强制转换不是将浮点数的二进制表示形式复制到整数。in 通过丢弃浮点数的小数部分,将浮点数(如何表示)转换为整数

您希望查看数字的二进制表示形式,您需要:

  1. 使用:union
uint32_t viaUnion(float x)
{
    union
    {
        uint32_t u32;
        float f;
    }un = {.f = x};

    return un.u32;
}
  1. 使用功能:memcpy
uint32_t viaMemcpy(float x)
{
    uint32_t u32;

    memcpy(&u32, &x, sizeof(u32));

    return u32;
}

要打印它:

uint32_t printViaUnion(float x)
{
    union
    {
        uint32_t u32;
        uint16_t u16[2];
        uint8_t u8[4];
        float f;
    }un = {.f = x};

    for(size_t i = 0; i < sizeof(x); i++)
    {
        if(i < sizeof(un.f) / sizeof(un.u8[0])) printf("u8[%zu] = % 3"PRIu8" 0x%02"PRIx8"\t", i, un.u8[i], un.u8[i]);
        if(i < sizeof(un.f) / sizeof(un.u16[0])) printf("u16[%zu] = % 6"PRIu16" 0x%04"PRIx16"\t", i, un.u16[i], un.u16[i]);
        if(i < sizeof(un.f) / sizeof(un.u32)) printf("u32[%zu] = % 10"PRIu32" 0x%08"PRIx32"\t", i, un.u32, un.u32);
        printf("\n");
    }

    return un.u32;
}


int main(void)
{
    printViaUnion(248.75f);
}

https://godbolt.org/z/sz1v1r1r4


u8[0] =   0 0x00    u16[0] =  49152 0xc000  u32[0] = 1131986944 0x4378c000  
u8[1] = 192 0xc0    u16[1] =  17272 0x4378  
u8[2] = 120 0x78    
u8[3] =  67 0x43    

-248.75


u8[0] =   0 0x00    u16[0] =  49152 0xc000  u32[0] = 3279470592 0xc378c000  
u8[1] = 192 0xc0    u16[1] =  50040 0xc378  
u8[2] = 120 0x78    
u8[3] = 195 0xc3
1赞 Lundin 10/19/2023 #2

基本上:因为这就是 C 语言所说的应该发生的事情。它与类型的底层二进制表示无关。

强制转换是显式转换,因此浮点数到整数转换的规则适用。C17 6.3.1.4:

当实数浮点类型的有限值转换为除 _Bool 以外的整数类型时,小数部分将被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为是未定义的。

如果你对纯二进制表示感兴趣,你可以使用指向字符的指针检查 C 中的任何类型:

float_t a = -248.75;
unsigned char* ptr = (unsigned char*)&a;
for(size_t i=0; i<sizeof(a); i++)
{
  printf("%.2X ", ptr[i]);
}

小端机上的输出:

00 C0 78 C3 

评论

0赞 0___________ 10/19/2023
指针双关语不能用于 char(有符号或无符号)以外的任何内容。我建议%.2hhX
1赞 Lundin 10/19/2023
@0____ 你认为是什么?根据默认参数 promotions,代码很好。 无论如何,不应该超出快速和肮脏的调试目的使用......unsigned char* ptrprintf
0赞 0___________ 10/19/2023
OP 可以假设他可以做他想做的事,而不仅仅是 char 表示。只需添加备注即可。short *sptr = (short *)&a
1赞 Lundin 10/19/2023
@0____原来我写了“您可以使用指向字符的指针检查 C 中的任何类型”,因为您必须使用指向字符的指针。
1赞 0___________ 10/19/2023
此外,浮点常数应为-248.75f
0赞 ikegami 10/20/2023 #3

混淆了转换值 () 和通过转换指针 () 访问。(Type)expr*(Type*)&obj

强制转换会导致转换。从浮点类型到积分的转换被定义为截断的结果。[C17§6.3.1.4¶1]

当实数浮点类型的有限值转换为 以外的整数类型时,小数部分将被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为是未定义的。_Bool

另一方面,当您投射指针时,您最终会得到一个指向同一地址的不同类型的指针。因此,您可以“欺骗”编译器将一种类型的对象作为另一种类型的对象进行访问。

因此,以下内容将实现您想要的:

float f = -248.75f;
uint32_t u32 = *(uint32_t*)f;

但是,这是未定义的行为。[C17§6.5¶6-7]但是,您也可以使用联合或 来实现此目的。memcpy

float f = -248.75f;
uint32_t u32 = ( union { float f; uint32_t u32; } ){ .f = f }.u32;