读书笔记——int到float的强制类型转换

最近在看一本名为<Computer System – A Programmer’s Perspective>的书。由于我所看过的计算机理论方面的书较少,加上自己大学期间一直也不用功,所以对于计算机的工作原理以及程序的工作方式我始终只知甚少,印象也十分模糊。

不过,应该说我碰到了一本好书。至少,通过昨晚对浮点数一章的阅读(呃…我的确之前对浮点数从没弄明白过),我终于了解了C语言中为什么32位int型数据强制转换到float型会出现精度不能完全保留

首先来看看我们可爱的int型变量吧,在一台典型的32位机器上一个有符号的int型的取值范围为- 2147483648 ~ 2147483647(-2^31 ~ (2^31-1))(注1)。也就是说,在一个4字节(32位2进制),除去首位用于符号位表示正负外,其余的31位都是数字的有效位。<>
下面再来看看“万恶的”float型变量:根据IEEE的浮点标准,一个浮点数应该用下述形式来表示:

V=(-1)^s * M * 2^E (公式1)

在C语言中,32位的float型变量有着这样的规定:首位表示符号位s,接下来的8位(指数域)用于表示2的指数E,剩余的23位(小数域)表示M(取值范围为[1,2)或[0,1))。除了上述规定以外,根据指数域的二进制表示情况不同,被编码的float型数字又可以分成三种情况——

1、规格化值。当指数域的8个二进制数字既非全零又非全1时,float数值就是这种情况。设指数域的八位二进制所表示的十进制数为e, 则公式1中的E就是 E = e – (2^7 – 1) (公式2);
而且此时,将小数域所表示的二进制假设为(f22)(f21)…(f1)(f0) (注2) ,则该小数域所表示的值即为f = 0.(f22)(f21)…(f1)(f0).于是M = 1 + f

2. 非规格化值。当指数域的8个二进制数字为全0时,float数值就为这种情况。这时指数域所表示的十进制数为0,规定指数值为 E = 1 – (2^7 – 1),也就是E为定值-126;此时小数域的值仍表示f = 0.(f22)(f21)…(f1)(f0),但是M的值却变成M = f。

3. 特殊值。当指数域的8个二进制数字为全1时即为这种情况。当小数域为全零时,该float值根据符号位的不同表示正无穷或者负无穷;当小数域为非全零时,该float值为NaN(Not a Number)。

以上,只是在C语言中对int和float的规约。具体在代码中执行强制类型转化究竟会发生什么?从下面两句很简单的语句开始:

int a = 3490593;
float b = (float)a;

那么在内存中a和b究竟存放的是什么值呢?

将a展开为二进制,其值为0000 0000 0011 0101 0100 0011 0010 0001,其十六进制即为0x00354321。 因为要转化为float型,所以首先要对上述二进制的表示形式改变为 M * 2^E 的形式.由于该数明显大于1,所以按照IEEE的标准,其浮点形势必然为规格化值。因此 ,转化后的形式为:

a = 1.101010100001100100001 * 2^21

根据 规格化值的定义,M = 1 + f. 所以f = 0.101010100001100100001.因为float型变量的小数域一共23位。所以b的最后23位可以得出,其值为 10101010000110010000100

下面再演绎指数域的值:因为a的指数表示法中,指数E = 21。根据公式2,e = E + (2^7 -1) = 148.所以可以得出b的指数域的二进制表示为:10010100。在加上原数为正,所以符号位s=0。

所以,可以得出b的二进制表示为0 10010100 10101010000110010000100。转化为十六位进制则是0x4A550C84。换句话说,它存储在内存中的值是与a是完全不同的。但是其间还是有关联性的——a的首位为1的数值位后的二进制表示是与b的小数域完全相同的。

很快,问题就出现了。 int型的有效位数是31,而float型小数域的有效位只有23位,也就是说如果上面的a的二进制的有效位超过了24位,那么float型的小数域的精度就不够了。因此必须进行舍入。比如:如果上面的a的二进制为0000 0001 1111 0101 0100 0011 0010 0001。这时b的小数域必须有24位才够,但是,这显然是不现实的,因此必须舍入到23位,舍入的原则是:所得结果的最低有效位为0。因此这个a在转换到float时,其精度就会丢失,因为该float的最后23位变成了11110101010000110010000——这显然是与原值不符的。

实际上,C语言中对于double型在32位机器上的小数域有52位,对于int型的31位有效位是绰绰有余了。这就是为什么大部分C语言教材上鼓励读者在执行强制类型转换时将int型转换成double。同时,这可能也是为什么int型能够直接隐式转换到 double型的缘故。

注1: y表示 x的y次方

注2:(fn)取0或1

P.S: 文中所用的int型数字3490593是<Computer System>一书第二章的课后习题2.34。写这篇读书笔记的同时顺便把这个练习题给做了。应该还是有点好处的,呵呵

Advertisements

About 小wing

☞ INTP星人☞爱猫家 ☞钝感男 ☞Google粉 ☞第70004号维基人 ☞民主自由控 ☞伪技术爱好者 ☞挨踢民工 ☞无证程序员 ☞游戏宅 ☞摇滚乐拥趸 ☞原版CD收藏癖 ☞反对爱国主义
此条目发表在技术分类目录,贴了, 标签。将固定链接加入收藏夹。

One Response to 读书笔记——int到float的强制类型转换

  1. DDR2K说道:

    8错,不过我想不用搞得这么清楚吧。float或者说double怎么存储的我倒不关心,但是从小数转换成整数,显然会把尾巴去掉,整数转化为小数,只要double的域大于int,就能完整的转化。而且double所占的字节数也要超过int,这是我不成熟的理解

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s