据我所知,将整数转换为浮点数需要将整数转换为二进制表示,通过寻找尾数、指数和分数来规范化整数值,然后从那里输出浮点数值?
至于浮点数到整数的转换,则需要将值分离成尾数、指数和分数,然后按照上述步骤反转以得到整数值?
我尝试遵循此问题的说明:Casting float to int (bitwise) in C。但我无法真正理解它。
还有,有人能解释一下为什么在将整数转换为浮点数时,大于23位的值需要进行四舍五入吗?
首先,如果想更好地理解浮点数陷阱,可以考虑阅读一篇论文:“计算机科学家应了解的有关浮点运算的知识”,http://www.validlab.com/goldberg/paper.pdf
接下来是正文。
以下代码是基本的,试图从范围在0 < value < 224的无符号整数中产生一个IEEE-754单精度浮点数。这是你在原问题中最有可能遇到的格式。
IEEE-754单精度浮点数分为三个字段:一个符号位、8位指数和23位有效数字(有时称为尾数)。IEEE-754使用一个“隐藏1”有效数字,这意味着实际上总共有24位有效数字。比特按从左到右的顺序打包,其中符号位在位31中,指数在位30 .. 23中,有效数字在位22 .. 0中。以下来自Wikipedia的示意图说明了此过程:
指数具有偏置值127,这意味着与浮点数相关联的实际指数比存储在指数字段中的值少127。因此,指数为0将被编码为127。
(注意:完整的维基百科文章可能对你有趣。参考:http://en.wikipedia.org/wiki/Single_precision_floating-point_format)
因此,将IEEE-754数字0x40000000解释为以下内容:
因此,该值为1.0 x 21 = 2.0。
因此,要将限定范围内的无符号整数转换为IEEE-754格式,可以使用下面的函数。它采取以下步骤:
reinterpret_cast
将结果的位模式转换为float
。这部分是一个丑陋的hack,因为它使用了类型强制转换指针。您还可以通过滥用union
来完成此操作。一些平台提供了内置操作(例如_itof
),以使此重新解释更少丑陋。有更快的方法来做到这一点; 这种方式旨在教学上有用,但不是超级高效的:
float uint_to_float(unsigned int significand)
{
// Only support 0 < significand < 1 << 24.
if (significand == 0 || significand >= 1 << 24)
return -1.0; // or abort(); or whatever you'd like here.
int shifts = 0;
// Align the leading 1 of the significand to the hidden-1
// position. Count the number of shifts required.
while ((significand & (1 << 23)) == 0)
{
significand <<= 1;
shifts++;
}
// The number 1.0 has an exponent of 0, and would need to be
// shifted left 23 times. The number 2.0, however, has an
// exponent of 1 and needs to be shifted left only 22 times.
// Therefore, the exponent should be (23 - shifts). IEEE-754
// format requires a bias of 127, though, so the exponent field
// is given by the following expression:
unsigned int exponent = 127 + 23 - shifts;
// Now merge significand and exponent. Be sure to strip away
// the hidden 1 in the significand.
unsigned int merged = (exponent << 23) | (significand & 0x7FFFFF);
// Reinterpret as a float and return. This is an evil hack.
return *reinterpret_cast< float* >( &merged );
}
你可以使用检测数字中前导1的函数来使这个过程更加高效。(有些函数叫做“clz”表示“计算前导零”,或者“norm”表示“规范化”。)BIC ..., #0x00800000
)。我最近没有编写过太多ARM汇编代码,但我认为这是有效的。 - Joe ZJoe Z的回答很优雅,但输入值的范围非常有限。32位浮点数可以存储以下范围内的所有整数值:
[-224...+224] = [-16777216...+16777216]
以及此范围之外的一些其他值。
整个范围将被覆盖:
float int2float(int value)
{
// handles all values from [-2^24...2^24]
// outside this range only some integers may be represented exactly
// this method will use truncation 'rounding mode' during conversion
// we can safely reinterpret it as 0.0
if (value == 0) return 0.0;
if (value == (1U<<31)) // ie -2^31
{
// -(-2^31) = -2^31 so we'll not be able to handle it below - use const
// value = 0xCF000000;
return (float)INT_MIN; // *((float*)&value); is undefined behaviour
}
int sign = 0;
// handle negative values
if (value < 0)
{
sign = 1U << 31;
value = -value;
}
// although right shift of signed is undefined - all compilers (that I know) do
// arithmetic shift (copies sign into MSB) is what I prefer here
// hence using unsigned abs_value_copy for shift
unsigned int abs_value_copy = value;
// find leading one
int bit_num = 31;
int shift_count = 0;
for(; bit_num > 0; bit_num--)
{
if (abs_value_copy & (1U<<bit_num))
{
if (bit_num >= 23)
{
// need to shift right
shift_count = bit_num - 23;
abs_value_copy >>= shift_count;
}
else
{
// need to shift left
shift_count = 23 - bit_num;
abs_value_copy <<= shift_count;
}
break;
}
}
// exponent is biased by 127
int exp = bit_num + 127;
// clear leading 1 (bit #23) (it will implicitly be there but not stored)
int coeff = abs_value_copy & ~(1<<23);
// move exp to the right place
exp <<= 23;
union
{
int rint;
float rfloat;
}ret = { sign | exp | coeff };
return ret.rfloat;
}
return *((float*)&ret);
是未定义行为(某些编译器如MSVC会定义该行为,但其他编译器可能会失败)。请使用memcpy
或union进行类型转换。 - Peter Cordesunsigned int
(或更好的是 uint32_t
)来移位/ OR FP 位模式也是一个好主意。左移有符号整数会改变符号位,这在技术上是有符号溢出 UB,我想。 (你实际上并没有这样做,而且还有足够多的其他假设,如二进制补码和 32 位 int
,可能并不重要。) - Peter Cordesreturn (float)INT_MIN;
是无意义的,因为这只是将 int
转换为 float
。此外,如果值是 unsigned
,它可以在代码中处理。 - S.S. Anne0.0
使用FP常量来返回所有零比特模式一样。两者都需要编译器在编译时知道如何生成FP比特模式。如果写成-2147483648.0f
可能会让你更加满意,而不是包含可能运行时的int->float转换并具有循环依赖性?注释仍然显示实际的比特模式。 - Peter Cordes您查看了IEEE 754浮点表示吗?
在32位归一化形式中,它有(尾数的)符号位、8位指数(过量-127,我想)和23位尾数在“十进制”中,除去“0.”(始终以此形式出现),基数为2,而不是10。也就是说:最高有效位的值为1/2,下一位的值为1/4,依此类推。
binary32
位模式强制转换为unsigned int
,而不是四舍五入到最近的整数。类型强制转换是实现软件浮点数的第一步,它通过指数将尾数移位以使小数点对齐到正确的位置。(实际上是基数点,因为这是二进制而不是十进制,所以单词“十进制”是错误的。) - Peter Cordes