提问人:BamsBamx 提问时间:10/19/2023 最后编辑:BamsBamx 更新时间:10/20/2023 访问量:116
为什么从 float 到 int 的转换会返回 float 的整数部分(在 C 中)?
Why does casting from float to int return the integer part of the float (in C)?
问:
众所周知,在将浮点数转换为 int 后,int 变量会获取浮点变量的整数部分,而丢弃小数部分。
以一个 32 位浮点数为例,其值为 -248.75,内存中的值如下:
float_t a = -248.75; // in binary: 11000011 01111000 11000000 00000000
如下图所示:
该二进制值以十进制表示以下值:
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:我问的是技术背景
答:
因为强制转换不是将浮点数的二进制表示形式复制到整数。in 通过丢弃浮点数的小数部分,将浮点数(如何表示)转换为整数
您希望查看数字的二进制表示形式,您需要:
- 使用:
union
uint32_t viaUnion(float x)
{
union
{
uint32_t u32;
float f;
}un = {.f = x};
return un.u32;
}
- 使用功能:
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
基本上:因为这就是 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
评论
%.2hhX
unsigned char* ptr
printf
short *sptr = (short *)&a
-248.75f
混淆了转换值 () 和通过转换指针 () 访问。(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;
评论
float
memcpy(&u32, &f32, sizeof u32);