文章目录
计算机进行小数运算时出错的原因
0、热身问题
- 二进制数0.1,用十进制数怎么表示?
0.5。 - 用小数点后有3位的二进制数,能表示十进制数0.625吗?
可以,表示为0.101。 - 将小数点分为符号、尾数、基数、指数4部分进行表现得形式称为什么?
浮点数,表示为“符号 尾数 x 基数的指数次幂”。 - 二进制的基数是多少?
2。 - 通过把0作为数值范围的中间值,不使用符号位的情况下就可以表示负数的表示方式称为什么?
EXCESS系统表现。 - 1010 1100 . 0101 0011 这个二进制数,用十六进制数表示的话是多少?
AC.53。
我们之前已经发表过一篇关于浮点数的博客——《计算机基础——浮点数的小细节》,当时是纯手写笔记,我觉得讲的很全但是不够直观。所以希望这篇文章,能尽量搞懂关于“小数”的知识。
这两篇博客配合起来看,也能更好地理解。
1、将0.1累加100次
首先,我们先来个小Java的程序例子,我们把0.1累加100次,看看结果是不是“0.1 x 100 = 10”。
@Test
public void test() {
double a = 0;
for (int i = 0; i < 100; i++) {
a += 0.1;
}
System.out.println("累加100次后的a:" + a);
}
后面我们就来解释一下,为什么“将0.1累加100次也得不到10”。
2、用二进制数表示小数
之前我们说了,计算机内的所有信息都是以二进制的形式处理的,那小数当然也不例外。
我们以1011.0011转换成十进制为例子。
3、计算机运算出错的原因
计算机运算小数出错的原因:有一个十进制的小数无法转换成二进制数。
我们以“小数点后4位二进制数”来举例子。
从下图我们就可以发现,有一些十进制的小数是不能表示出来的。比如说十进制的0.1,其二进制表示为0.000 1100 1100……(1100循环)。
而计算机这个功能有限的设备,是无法处理无限循环的小数的。所以在计算的时候,计算机就会根据变量所对应的长度讲数值进行截断或者四舍五入。
也就出现了“0.1累加100次不是10”这个“错误”了。
4、什么是浮点数
像1011.0011这种带点小数的表达形式,我们人类自己的一种“书面表达方式”,计算机它可看不懂。
计算机内部使用“浮点数”来表示小数。
浮点数的组成
1、基数 --> 计算机内部使用二进制 --> 基数为2
2、符号 --> 0表示正;1表示负
3、尾数 --> 将小数点前面的值固定为1的正则表达式
4、指数 --> EXCESS系统表现
浮点数的内部构造(IEEE标准)
双精度浮点数类型用64位,单精度浮点数类型用32位。
5、正则表达式和EXCESS系统
5.1、正则表达式
尾数部分使用“正则表达式”,将小数点前面的值固定为1的正则的表达式。
将二进制表示的小数左移或者右移(逻辑右移),整数部分的第1位变为1,第2位之后变为0。
整数使用包含最高位(即符号位)在内的 全体二进制来表示数值。
浮点数由符号部分、尾数部分和指数部分这三部分独立的数值组合而成。
5.2、EXCESS系统
EXCESS系统:通过把表示范围的中间值设为0,使得负数不需要使用符号位来表示。
指数部分使用EXCESS系统,主要是为了表示负数时不使用符号位。
以单精度浮点数为例,其指数部分占8位。
6、在实际的程序中进行确定
我们看一个C语言的小程序,源码如下:
#include<stdio.h>
int main() {
float data = 0.75;
printf("%p\n", &data);
printf("%f\n", data);
return 0;
}
我们通过VS2022的内存监控来看0.75这个数在底层是在怎么存储的。
再通过一个Java的程序来验证0.75的二进制为0.11。
@Test
public void test() {
double a = 0.75;
double num = a;
StringBuilder sb = new StringBuilder("0.");
while (num > 0) {
//1、乘2
double r = num * 2;
//2、取整
//判断整数部分
if (r >= 1) {
//整数部分为1
//消除整数部分
num = r - 1;
sb.append("1");
} else {
//整数部分为0
num = r;
sb.append("0");
}
//为何是大于34呢?因为题目是32位二进制,加上"0."两位
if (sb.length() > 34) {
System.out.println("ERROR");
return;
}
}
System.out.println("十进制:" + a);
System.out.println("二进制:" + sb.toString());
}
运行结果:
十进制:0.75
二进制:0.11
7、如何避免计算机计算出错
两种方法:
- 回避策略,即无视它。
- 小数转成整数,计算之后再转回小数。比如,先扩大1000倍计算,之后再除以1000。
8、二进制和十六进制
十六进制,以“0x”或“0X”表示。
八进制,以“0O”或者“0o”表示(0和字母Oo)。
二进制,以“0B”或“0b”表示。
1位十六进制 = 4位二进制
举例:
0x 3D CC CC CD ---> 0b 0011 1101 1100 1100 1100 1100 1100 1101
0b 1011.0110 ---> 0x B.6
注:如有错误,敬请指正。