将int转换为float时在后台会发生什么

我对于如何逐步将int转换为浮动有一些不了解? 假设我有一个二进制格式的带符号整数。 而且,我想把它用手漂浮。 但是,我不能。 因此,CAn一个人告诉我如何逐步进行转换?

我在c中进行多次转换? 喜欢;

int a = foo ( ); float f = ( float ) a ; 

但是,我还没弄清楚背景会发生什么。 而且,为了更好地理解,我想手工完成转换。

编辑:如果您对转换了解很多,您还可以提供有关浮动到双转换的信息。 而且,对于float到int

浮点值(IEEE754,无论如何)基本上有三个组成部分:

  • 标志s ;
  • 一系列指数位e ; 和
  • 一系列尾数位m

精度决定了指数和尾数有多少位可用。 让我们检查单精度浮点的值0.1:

 s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm 1/n 0 01111011 10011001100110011001101 

|+- 8388608

+– 4194304

||+— 2097152

|+—- 1048576

+—– 524288

||+—— 262144

|+——- 131072

+——– 65536

||+——— 32768

|+———- 16384

+———– 8192

||+———— 4096

|+————- 2048

+————– 1024

||+————— 512

|+—————- 256

+—————– 128

||+—————— 64

|+——————- 32

+——————– 16 ||+——————— 8 |+———————- 4 +———————– 2

标志是积极的,这很容易。

指数为64+32+16+8+2+1 = 123 - 127 bias = -4 ,因此乘数为2 -41/16 。 偏见是存在的,这样你就可以获得非常小的数字(如10 -30 )以及大数字。

尾数很粗糙。 它由1 (隐式基数)加上(对于所有这些位,每个值为1 /(2 n ),因为n1开始并向右增加), {1/2, 1/16, 1/32, 1/256, 1/512, 1/4096, 1/8192, 1/65536, 1/131072, 1/1048576, 1/2097152, 1/8388608}

当你添加所有这些,你得到1.60000002384185791015625

当你乘以2 -4乘数时,得到0.100000001490116119384765625 ,这就是为什么他们说你不能完全代表0.1作为IEEE754浮点数。

整数转换为浮点数方面,如果尾数中包含尽可能多的位(包括隐式1),则只需传输整数位模式并选择正确的指数即可。 不会有精度损失。 例如,双精度IEEE754(64位,尾数为52/53)对32位整数没有任何问题。

如果整数中有更多位(例如32位整数和32位单精度浮点数,只有23/24位尾数),则需要缩放整数。

这涉及剥离最低有效位(实际上舍入),以使其适合尾数位。 这当然会导致精度损失,但这是不可避免的。


我们来看看具体的值123456789 。 以下程序转储每种数据类型的位。

 #include  static void dumpBits (char *desc, unsigned char *addr, size_t sz) { unsigned char mask; printf ("%s:\n ", desc); while (sz-- != 0) { putchar (' '); for (mask = 0x80; mask > 0; mask >>= 1, addr++) if (((addr[sz]) & mask) == 0) putchar ('0'); else putchar ('1'); } putchar ('\n'); } int main (void) { int intNum = 123456789; float fltNum = intNum; double dblNum = intNum; printf ("%d %f %f\n",intNum, fltNum, dblNum); dumpBits ("Integer", (unsigned char *)(&intNum), sizeof (int)); dumpBits ("Float", (unsigned char *)(&fltNum), sizeof (float)); dumpBits ("Double", (unsigned char *)(&dblNum), sizeof (double)); return 0; } 

我系统的输出如下:

 123456789 123456792.000000 123456789.000000 integer: 00000111 01011011 11001101 00010101 float: 01001100 11101011 01111001 10100011 double: 01000001 10011101 01101111 00110100 01010100 00000000 00000000 00000000 

我们会一次看这些。 首先是整数,简单的两个幂:

  00000111 01011011 11001101 00010101 

| || || || || | | | +-> 1

| || || || || | | +—> 4

| || || || || | +—–> 16

| || || || || +———-> 256

| || || || |+————> 1024

| || || || +————-> 2048

| || || |+—————-> 16384

| || || +—————–> 32768

| || |+——————-> 65536

| || +——————–> 131072

| |+———————-> 524288

| +———————–> 1048576

+————————-> 4194304 ||+—————————-> 16777216 |+—————————–> 33554432 +——————————> 67108864 ========== 123456789

现在让我们看一下单精度浮点数。 注意尾数的匹配模式匹配整数作为近乎完美的匹配:

 mantissa: 11 01011011 11001101 00011 (spaced out). integer: 00000111 01011011 11001101 00010101 (untouched). 

在尾数的左边有一个隐含的 1位,它在另一端也被舍入,这是精度损失的来源(从上面那个程序的输出中,值从123456789变为123456792 )。

制定价值观:

 s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm 1/n 0 10011001 11010110111100110100011 || | || 

| || | |+- 8388608 || | ||

| || | +– 4194304 || | ||

| || +—— 262144 || | ||

| |+——– 65536 || | ||

| +——— 32768 || | ||

+———— 4096 || | || ||+————- 2048 || | || |+————– 1024 || | || +————— 512 || | |+—————– 128 || | +—————— 64 || +——————– 16 |+———————- 4 +———————– 2

标志是积极的。 指数为128+16+8+1 = 153 - 127 bias = 26 ,因此乘数为2 2667108864

尾数是1 (隐式基数)加(如上所述), {1/2, 1/4, 1/16, 1/64, 1/128, 1/512, 1/1024, 1/2048, 1/4096, 1/32768, 1/65536, 1/262144, 1/4194304, 1/8388608} 。 当你添加所有这些,你得到1.83964955806732177734375

当你乘以2 26乘数时,得到123456792 ,与程序输出相同。

双位掩码输出是:

 s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm 0 10000011001 1101011011110011010001010100000000000000000000000000 

不会经历弄清楚那个野兽的价值的过程:-)但是,我在整数格式旁边显示尾数来显示公共位表示:

 mantissa: 11 01011011 11001101 00010101 000...000 (spaced out). integer: 00000111 01011011 11001101 00010101 (untouched). 

您可以再次看到左侧隐含位的共性和右侧的更大位可用性,这就是为什么在这种情况下不会丢失精度。


在浮动和双打之间转换方面,这也很容易理解。

首先必须检查特殊值,如NaN和无穷大。 这些由特殊的指数/尾数组合表示,并且可能更容易检测这些前置角度,以新格式生成等效物。

那么在你从double到float的情况下,你显然可用的范围较小,因为指数中的位数较少。 如果你的双精度超出浮动范围,你需要处理它。

假设它适合,那么你需要:

  • 重新指数(这两种类型的偏差是不同的)。
  • 从尾数中复制尽可能多的位(如果需要,可以舍入)。
  • 用零位填充剩余的目标尾数(如果有的话)。

从概念上讲,这很简单。 float (在IEEE 754-1985中)具有以下表示:

  • 1位符号
  • 8位指数(0表示非规范化数字,1表示-126,127表示0,255表示无穷大)
  • 23位尾数(“1.”后面的部分)

所以基本上它大致是:

  • 确定数字的符号和大小
  • 找到24个最有效位,正确舍入
  • 调整指数
  • 将这三个部分编码为32位forms

在实现您自己的转换时,它很容易测试,因为您只需将结果与内置类型转换运算符进行比较。