三、计算机处理小数时出现错误的原因
3.1用二进制表示小数
二进制:11.101 小数点后表示2的 - XX次方
十进制:10.52 小数点后表示10 的 -XX次方
3.2二进制永远无法表示十进制的0.1
如同十进制无法表示1/3一样,二进制表格如下:
二进制数 | 十进制数 |
---|---|
0.0000 | 0 |
0.0001 | 0.0625 |
0.0010 | 0.125 |
2的负多少次方,无论怎么累加都无法得到0.1
3.3什么是浮点数
定点数:321.123 小数点是固定的;
浮点数:0.123x10-8 小数点不是固定的,按照后面的来确定小数点的位置
浮点数的组成:对于计算机来说,基数是确定的,为2,所以只用三个参数来表示浮点数:符号,尾数,指数
双精度与单精度浮点数的构成:
双精度:
单精度:
3.4正则表达式和excess系统
这里的正则表达式:指的是只能用一种特定形式去表示小数,如十进制的科学计数法所规定的一样,二进制也有特定的规则
浮点数的正则表达式:将小数点前面的值固定为1的规则,让后将1去掉,完成正则,得到尾数部分
指数部分该如何确定:EXCES
如单精度的指数部分有8位,最大能表示的为1111 1111 (十进制的255),取它的1/2 ,即0111 1111 =127(小数部分舍弃)表示0;
举个例子:1~10有十个数字,如果取中间的5作为0, 那么10就表示:10-5 = +5 ,1就代表:1 - 5 = -4 ;这就是所谓的EXCESS系统
在实际程序中看负数到底是怎么表示的:
#include<stdio.h>
#include<string.h>
int main() {
float data;
unsigned long buff;
int i;
char s[34];
// 将0.75以单精度的形式保存起来
data = (float)0.75;
// 将数据复制到4个字节长度的整数变量buff中,然后逐一提取每一位
// memcpy(viod* des, void* src, unsigned int count)
// 将src 指针所指的内容复制到dec所指向的位置, 大小为count个字节
memcpy(&buff, &data, 4);
// buff不管0.75的二进制格式(符号位-指数位-尾数位),直接同一对待
// 看这个0.75二进制码以十进制读出来
printf("%lu\n",buff);
// 逐一提取每一位
for (i =33; i >=0; i-- ){
if(i==1 || i == 10)
// 在符号位,指数位与尾数位之间加入-分界符来使得表示更加清晰
s[i]= '-';
else{
// 将buff里的数转换为2进制
if(buff % 2 == 1){
s[i]='1';
} else{
s[i] = '0';
}
buff /=2;
}
}
s[34]='\0';
printf("%s\n",s);
}
结果如下:
符号位:0 因为0.75是个正数
尾数部分1000…还原为小数点前为1的形式:1.10000…. = 1* 2的0次幂 + 1*2的-1次幂 = 1.5
指数部分 0111 1110 = 126 即126 - 127 = -1;
最终结果 = 尾数部分 x 指数部分 :1.5* 0.5(2的-1次方) = 0.75
3.5 如何避免出错
第一种策略:回避策略:允许一定程度的误差
第二种策略:涉及到金融时,先将小数转换为整数,因为整数计算一定不存在错误,将结果再转换成小数显示出来