多的是,你不知道的“浮点数”

问题现象
      有人问了我这样一个问题图片如下。

很多人的疑问是程序的运行结果是9,不应该是8吗?我们可以在程序中运行这样一句代码

[Java]  纯文本查看  复制代码
?
System.out.println( 0.1 + 0.7 );

程序运行的结果是0.7999999999999999。 这个数明显是小于0.8的,所以就有了上图的运行结果。那么为什么会出现这种现象呢?
问题原理      
其实计算机中很多浮点数本身就是近似值,因为他们不能被精确的表示。这源于计算机的底层是二进制的,比如0.1 。0.1为什么不能精确的表示呢?在十进制的世界里时可以精确表示的,但是在二进制中则不行。下面我们举个例子。比如78.912,是这样表示的。7*10+8*1+9*0.1+1*0.01+2*0.001,小数的每一位也有位权,切换为科学计数法,则是7*10^1+8*10^0+9*10^(-1)+1*10^(-2)+2*10^(-3)。 虽然很多数在10进制里可以精确表示,但是也有很多数在二进制里面依然不能精确表示,比如10/3 近似为3.33 无论多少位小数,也不能精确表示10/3, 只能是近似为3.33。      其实二进制也是类似的,二进制的小数用科学计数法来表示的话,先来看看2的某某次方对应的10进制数据吧。  所以我们就能理解了 十进制的0.1 = 1*2^(-?)+1*2^(-?)+1*2^(-?)+…. ,无论怎么相加,最后也只能无限接近于0.1 ,而不是真正的0.1 。      那有人会问,程序中确实显示的是0.1,这只是Java语言给我们造成的假象,计算结果其实也是不精确的,但是由于结果和0.1足够接近,在输出的时候,Java选择了输出0.1这个看上去非常精简的数字,而不是一个中间有很多0的小数。在误差足够小的时候,结果看上去是精确的,但不精确其实才是常态。      二进制中表示小数,也采用科学计算方法,形如m*(2^e)。 m称为尾数,e称为指数。指数可以为正,也可为负,计算机中的浮点数单独表示尾部分和指数部分,外加一个符号位。几乎所有的硬件和语言表示的小数的二进制格式都是一样的,这是一种标准,叫做IEEE754标准,他定义了两种格式,一种是32位的,对应于Java的float,另一种是64位的,对应于Java的double,32位格式中,1位表示符号,23位表示尾数,8位表示指数。64位格式中,1位表示符号,52位表示尾数,11位表示指数。IEEE754中还有一些复杂的细节,难以理解,而且不常用,所以本文暂时先不介绍了。      也有人会问,为什么计算机底层的数据为什么非要用二进制去表示呢?计算机无非是一个电器,里面有无数的电阻和电容,当计算机通交流电之后,遇到电容是低电压用0表示,遇到电阻是高电压用1表示,所以计算机的底层只能是二进制,因为他只能表示高低电压。这是模拟电路和数字电路的知识。

问题解决方案   
方案1: 使用浮点数计算的时候,我们可以把小数变成int或者是long,计算完毕后可以还原float或者double类型,举个例子:比如计算0.1-0.9的和,可以如下图这样计算。

[Java]  纯文本查看  复制代码
?
1
2
3
4
5
6
//求1-9的和 最后结果再除以10
int sum = 0 ;
for ( int i = 1 ; i <= 9 ; i++) {
     sum+=i;
}
System.out.println(sum/ 10.0 ); //4.5

方案2: 使用java.math.BigDecimal这个类来运算。 比如0.1+0.7,请看下列代码。

[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
package cam.itheima.demo;
import java.math.BigDecimal;
public class Demo5 {
     public static void main(String[] args) {
         BigDecimal bd1 = new BigDecimal( "0.1" );
         BigDecimal bd2 = new BigDecimal( "0.7" );
         BigDecimal sum = bd1.add(bd2);
         System.out.println(sum);
     }
}

这两种方案各有利弊,方案一速度快不消耗太多的内存,但是如果数据过大,int和long类型表示不了。方案二可以表示很大的数据,但是速度比较慢内存消耗比较大。总结:      本文讲述了Java中的浮点数不精确的现象,并讲述了计算机底层表示浮点数的原理从而解释了浮点数不精确的原因,最后提出了浮点数参与精确运算的解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值