C 位级 Int 到 Float 转换意外输出
C Bit-Level Int to Float Conversion Unexpected Output
背景:
我正在玩位级编码(这不是家庭作业 - 只是好奇)。我在网上和一本名为 Hacker's Delight 的书中找到了很多好的 material,但是我遇到了一个在线问题。
它要求将整数转换为浮点数。我使用以下链接作为解决问题的参考:
How to manually (bitwise) perform (float)x?
How to convert an unsigned int to a float?
http://locklessinc.com/articles/i2f/
问题与疑问:
我以为我已经足够了解这个过程(我试图在评论中记录这个过程),但是当我测试它时,我不明白输出。
测试用例:
float_i2f(2) returns 1073741824
float_i2f(3) returns 1077936128
我希望看到类似 2.0000 和 3.0000 的结果。
我是不是把转换搞砸了?我想这可能是一个内存地址,所以我想也许我在访问实际数字所需的转换步骤中遗漏了一些东西?或者也许我打印不正确?我正在这样打印我的输出:
printf("Float_i2f ( %d ): ", 3);
printf("%u", float_i2f(3));
printf("\n");
但我认为打印方法对于 C 中的无符号值很好(我习惯在 Java 中编程)。
感谢任何建议。
代码:
/*
* float_i2f - Return bit-level equivalent of expression (float) x
* Result is returned as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point values.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_i2f(int x) {
if (x == 0){
return 0;
}
//save the sign bit for later and get the asolute value of x
//the absolute value is needed to shift bits to put them
//into the appropriate position for the float
unsigned int signBit = 0;
unsigned int absVal = (unsigned int)x;
if (x < 0){
signBit = 0x80000000;
absVal = (unsigned int)-x;
}
//Calculate the exponent
// Shift the input left until the high order bit is set to form the mantissa.
// Form the floating exponent by subtracting the number of shifts from 158.
unsigned int exponent = 158; //158 possibly because of place in byte range
while ((absVal & 0x80000000) == 0){//this checks for 0 or 1. when it reaches 1, the loop breaks
exponent--;
absVal <<= 1;
}
//find the mantissa (bit shift to the right)
unsigned int mantissa = absVal >> 8;
//place the exponent bits in the right place
exponent = exponent << 23;
//get the mantissa
mantissa = mantissa & 0x7fffff;
//return the reconstructed float
return signBit | exponent | mantissa;
}
从评论继续。您的代码是正确的,您只是在查看由 IEEE-754 单精度浮点数中的位组成的 等价物 unsigned integer
。 IEEE-754 单精度数字格式(由符号、扩展指数和尾数组成)可以解释为 float
,或者那些相同的位可以解释为 unsigned integer
(仅由 32 位组成的数字)。您正在输出浮点数的 无符号等价物 。
你可以用一个简单的并集来确认。例如:
#include <stdio.h>
#include <stdint.h>
typedef union {
uint32_t u;
float f;
} u2f;
int main (void) {
u2f tmp = { .f = 2.0 };
printf ("\n u : %u\n f : %f\n", tmp.u, tmp.f);
return 0;
}
示例Usage/Output
$ ./bin/unionuf
u : 1073741824
f : 2.000000
如果您还有其他问题,请告诉我。很高兴看到您的研究得出了正确的浮点转换结果。 (另请注意关于 truncation/rounding 的第二条评论)
我只是在这里插话,因为没有解决任何关于字节序的具体问题。那我们就来说说吧。
原始问题中值的构造与字节顺序无关,使用移位和其他按位运算。这意味着无论您的系统是大端还是小端,实际值都是相同的。区别在于它在内存中的字节顺序。
IEEE-754 普遍接受的约定是字节顺序是大端字节序(尽管我相信没有正式的规范,因此没有要求实现遵循它)。这意味着如果您想直接将整数值解释为浮点数,则需要按大端字节顺序排列。
因此,如果且仅当您知道系统上浮点数和整数的字节顺序相同时,您可以将此方法与联合结合使用.
在常见的基于 Intel 的架构上,这是不行的。在这些架构上,整数是小端,浮点数是大端。您需要将您的价值转换为大端。一个简单的方法是重新打包它的字节 即使它们已经是大端:
uint32_t n = float_i2f( input_val );
uint8_t char bytes[4] = {
(uint8_t)((n >> 24) & 0xff),
(uint8_t)((n >> 16) & 0xff),
(uint8_t)((n >> 8) & 0xff),
(uint8_t)(n & 0xff)
};
float fval;
memcpy( &fval, bytes, sizeof(float) );
我要强调的是,如果您尝试将整数表示重新解释为 float
或相反,您只需要担心这一点。
如果您只是想输出以位为单位的表示形式,则无需担心。您可以只以有用的形式显示整数,例如十六进制:
printf( "0x%08x\n", n );
背景:
我正在玩位级编码(这不是家庭作业 - 只是好奇)。我在网上和一本名为 Hacker's Delight 的书中找到了很多好的 material,但是我遇到了一个在线问题。
它要求将整数转换为浮点数。我使用以下链接作为解决问题的参考:
How to manually (bitwise) perform (float)x?
How to convert an unsigned int to a float?
http://locklessinc.com/articles/i2f/
问题与疑问:
我以为我已经足够了解这个过程(我试图在评论中记录这个过程),但是当我测试它时,我不明白输出。
测试用例:
float_i2f(2) returns 1073741824
float_i2f(3) returns 1077936128
我希望看到类似 2.0000 和 3.0000 的结果。
我是不是把转换搞砸了?我想这可能是一个内存地址,所以我想也许我在访问实际数字所需的转换步骤中遗漏了一些东西?或者也许我打印不正确?我正在这样打印我的输出:
printf("Float_i2f ( %d ): ", 3);
printf("%u", float_i2f(3));
printf("\n");
但我认为打印方法对于 C 中的无符号值很好(我习惯在 Java 中编程)。
感谢任何建议。
代码:
/*
* float_i2f - Return bit-level equivalent of expression (float) x
* Result is returned as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point values.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_i2f(int x) {
if (x == 0){
return 0;
}
//save the sign bit for later and get the asolute value of x
//the absolute value is needed to shift bits to put them
//into the appropriate position for the float
unsigned int signBit = 0;
unsigned int absVal = (unsigned int)x;
if (x < 0){
signBit = 0x80000000;
absVal = (unsigned int)-x;
}
//Calculate the exponent
// Shift the input left until the high order bit is set to form the mantissa.
// Form the floating exponent by subtracting the number of shifts from 158.
unsigned int exponent = 158; //158 possibly because of place in byte range
while ((absVal & 0x80000000) == 0){//this checks for 0 or 1. when it reaches 1, the loop breaks
exponent--;
absVal <<= 1;
}
//find the mantissa (bit shift to the right)
unsigned int mantissa = absVal >> 8;
//place the exponent bits in the right place
exponent = exponent << 23;
//get the mantissa
mantissa = mantissa & 0x7fffff;
//return the reconstructed float
return signBit | exponent | mantissa;
}
从评论继续。您的代码是正确的,您只是在查看由 IEEE-754 单精度浮点数中的位组成的 等价物 unsigned integer
。 IEEE-754 单精度数字格式(由符号、扩展指数和尾数组成)可以解释为 float
,或者那些相同的位可以解释为 unsigned integer
(仅由 32 位组成的数字)。您正在输出浮点数的 无符号等价物 。
你可以用一个简单的并集来确认。例如:
#include <stdio.h>
#include <stdint.h>
typedef union {
uint32_t u;
float f;
} u2f;
int main (void) {
u2f tmp = { .f = 2.0 };
printf ("\n u : %u\n f : %f\n", tmp.u, tmp.f);
return 0;
}
示例Usage/Output
$ ./bin/unionuf
u : 1073741824
f : 2.000000
如果您还有其他问题,请告诉我。很高兴看到您的研究得出了正确的浮点转换结果。 (另请注意关于 truncation/rounding 的第二条评论)
我只是在这里插话,因为没有解决任何关于字节序的具体问题。那我们就来说说吧。
原始问题中值的构造与字节顺序无关,使用移位和其他按位运算。这意味着无论您的系统是大端还是小端,实际值都是相同的。区别在于它在内存中的字节顺序。
IEEE-754 普遍接受的约定是字节顺序是大端字节序(尽管我相信没有正式的规范,因此没有要求实现遵循它)。这意味着如果您想直接将整数值解释为浮点数,则需要按大端字节顺序排列。
因此,如果且仅当您知道系统上浮点数和整数的字节顺序相同时,您可以将此方法与联合结合使用.
在常见的基于 Intel 的架构上,这是不行的。在这些架构上,整数是小端,浮点数是大端。您需要将您的价值转换为大端。一个简单的方法是重新打包它的字节 即使它们已经是大端:
uint32_t n = float_i2f( input_val );
uint8_t char bytes[4] = {
(uint8_t)((n >> 24) & 0xff),
(uint8_t)((n >> 16) & 0xff),
(uint8_t)((n >> 8) & 0xff),
(uint8_t)(n & 0xff)
};
float fval;
memcpy( &fval, bytes, sizeof(float) );
我要强调的是,如果您尝试将整数表示重新解释为 float
或相反,您只需要担心这一点。
如果您只是想输出以位为单位的表示形式,则无需担心。您可以只以有用的形式显示整数,例如十六进制:
printf( "0x%08x\n", n );