1. 将0.1累加100次也得不到10
举一个计算机运算错误(无法得到正确结果)的例子:
![](https://i-blog.csdnimg.cn/blog_migrate/808ea98a523791561461d3600345d32b.png)
0.1累加100次的结果是10,但上述代码运行后,显示的结果并不是10。
10.100002
2. 用二进制数表示小数
把1011.0011这个有小数点的二进制数转换成十进制数。
将各数位的数值和位权相乘的结果相加即可,如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/5f94ba721efa15cb148ca0f972f4387e.png)
3. 计算机运算出错的原因
计算机之所以会出现运算错误,是因为“有一些十进制数的小数无法转换成二进制数”。
代码清单3-1中程序无法得到正确结果的原因是,因为无法正确表示的数值,最后都变成了近似值。
计算机这个功能有限的机器设备,是无法处理无限循环的小数的。
因此,在遇到循环小数时,计算机就会根据变量数据类型所对应的长度将数值从中间截断或者四舍五入。
4. 什么是浮点数
很多编程语言中都提供了两种表示小数的数据类型,分别是双精度浮点数和单精度浮点数。
双精度浮点数类型用64位、单精度浮点数类型用32位来表示全体小数。
在C语言中,双精度浮点数类型和单精度浮点数类型分别用double和float来表示。
浮点数是指用符号、尾数、基数和指数这四部分来表示的小数(图3-3)。
因为计算机内部使用的是二进制数,所以基数自然就是2、因此,实际的数据中往往不考虑基数,只用符号,尾数,指数这三部分即可表示浮点数。
![](https://i-blog.csdnimg.cn/blog_migrate/fa0d40295e5662aac1b4b9bff7aa7088.png)
![](https://i-blog.csdnimg.cn/blog_migrate/22b53b176599f9a242a78f671a236004.png)
浮点数的表现方式,使用最为普遍的IEEE标准。
双精度浮点数和单精度浮点数在表示同一个数值时使用的位数不同。
此外,双精度浮点数能够表示的数值范围要大于单精度浮点数。
符号部分是指使用一个数据位来表示数值的符号。
该数据位是1时表示负,为0时则表示“正或者0”。
这和用二进制数来表示整数时的符号位是同样的。数值的大小用尾数部分和指数部分来表示。
尾数部分用的是“将小数点前面的值固定为1的正则表达式”,而指数部分用的则是“EXCESS系统表现”。
5. 正则表达式和EXCESS系统
尾数部分使用正则表达式,可以将表现形式多样的浮点数统一为一种表现形式。
![](https://i-blog.csdnimg.cn/blog_migrate/54854c10d045617982331c7399cfddc5.png)
例如,十进制数的浮点数应该避循“小数点前面是0,小数点后面第1位不能是0”这样的规则。
根据这个规则,0.75就是“0.75×10的0次幂”,也就是说,只能用尾数部分是0.75,指数部分是0这个方法来表示。
根据这个规则来表示小数的方式,就是正则表达式。
二进制数也是同样的道理。
在二进制数中,使用的是“将小数点前面的值固定为1的正则表达式“。
具体来讲,就是将二进制数表示的小数左移或右移(这里是逻辑移位。因为符号位是独立的)数次后,整数部分的第1位变为1,第2位之后都变为0。而且,第1位的1在实际的数据中不保存。
由于第1位必须是1,因此,省略该部分后就节省了一个数据位,从而可以表示更多的数据范围(虽不算太多),
单精度浮点数的正则表达式的具体例子如图3-6所示。
单精度浮点数中,尾数部分是23位,但由于第1位的1被省略了,所以实际上可以表示24位的数值。
![](https://i-blog.csdnimg.cn/blog_migrate/bffa39ab57b66998e392b14c51e91662.png)
指数部分中使用的EXCESS系统,使用这种方法主要是为了表示负数时不使用符号位。
在某些情况下在指数部分,需要通过“负oo次”的形式来表示负数。
EXCESS系统表现是指,通过将指数部分表示范围的中间值设为0,使得负数不需要用符号来表示。
也就是说,当指数部分是8位单精度浮点数时最大值11111111=255的1/2,即01111111=127(小数部分舍弃)表示的是0,
指数部分是11位双精度浮点数时,11111111111=2047的1/2,即01111111111=1023(小数部分舍弃)表示的是0。
举例来进行说明。假设有这样一个游戏,用113(AK)的扑克牌来表示负数。
这时,可以把中间的7这张牌当成0。如果扑克牌7是0,10就表示+3,3就表示-4。
计算机计算出错的原因之一是,采用浮点数来处理小数。
为了避免计算出错,介绍两种处理方法:
首先是回避策略,即无视这些错误。根据程序目的的不同,有时一些微小的偏差并不会造成什么问题。
另一个策略是把小数转换成整数来计算。计算机在进行小数计算时可能会出错,但进行整数计算(只要不超过可处理的数值范围)时一定不会出现问题。因此,进行小数的计算时可以暂时使用整数,然后再把计算结果用小数表示出来即可。
6. 二进制数和十六进制数
在以位为单位表示数据时,使用二进制数很方便,但如果位数太多,看起来就比较麻烦。
因此,在实际程序中,也经常会用十六进制数来代替二进制数。
在C语言程序中,只需在数值的开头加上0x(0和x)就可以表示十六进制数。
二进制数的4位,正好相当于十六进制数的1位:
例如,32位二进制数00111101110011001100110011001101用十六进制数来表示,就是3DCCCCCD这个8位数。
由此可见,通过使用十六进制数,二进制数的位数能够缩短至原来的1/4。位数变少之后,看起来也就更清晰了(图3-9)。
![](https://i-blog.csdnimg.cn/blog_migrate/2b484ec22eabc3c13b480afd0cd86025.png)
用十六进制数来表示二进制小数时,小数点后的二进制数的4位也同样相当于十六进制数的1位。不够4位时用0填补二进制数的低位即可。
例如,101.011的低位补0后为101.0110,这时就可以表示为十六进制数B.6。
十六进制数的小数点后第1位的位权是 1 6 − 1 16^{-1} 16−1即1/16=0.0625。
参考
《程序是怎样跑起来的》 —— 3. 计算机进行小数运算时出错的原因