浮点精度问题是怎么产生的?

浮点精度问题是怎么产生的

对于小数的运算,相信大家都有遇到过精度丢失问题,利于0.1+0.2得到的是0.30000000000000004而不是0.3,那么如何解释为什么计算机中 0.2 + 0.1 不等于 0.3 呢?在剖析这个问题之前我们要先理解IEEE754标准。

什么是IEEE754?下面是一段官方的解释了解即可,重点是我们要关注IEEE754存储格式。

概念:IEEE二进制浮点数算术标准(IEEE754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number),一些特殊数值(无穷∞与非数值NaN),以及这些数值的“浮点数运算符”。 常见的四种浮点数值表示方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)。C语言的float通常是指IEEE单精确度,而double是指双精确度。

存储格式:IEEE 754标准准确地定义了单精度和双精度浮点格式

单精度浮点格式(32 位)。

双精度浮点格式(64 位)。

EEE754 标准中规定 float 单精度浮点数在机器中表示用 1 位表示数字的符号,用 8 位表示指数,用 23 位表示尾数,即小数部分。对于 double 双精度浮点数,用 1 位表示符号,用 11 位表示指数,52 位表示尾数,其中指数域称为阶码。IEEE754 浮点数的格式如下图所示。

下面以0.1和0.2为例将其转换为IEEE754的格式存储到内存中

0.1这个浮点数转换成一个二进制数0.00011001100110011...

0.2这个浮点数转换成一个二进制数0.0011001100110011...

这两个浮点数无法精确转换成一个二进制数,由于在内存中表示精度有限必须舍弃后面的尾数部分。

单精度为例0.1和0.2转换为IEEE754格式以如下

0.1 : 0 01111011 10011001100110011001100

0.2:  0 01111100 10011001100110011001100

所以我们看到0.1+0.2得到的是0.300000004而不是0.3的结论,而造成这个精度问题的根本原因在于不是所有的数字(如例子中的0.1和0.2)都可以用二进制表示而进行截断,造成精度丢失。

但是尽管如此我们还是有办法在运算是避免精度问题,那就是BigDecimal,那么BigDecimal是怎么解决这个问题的呢?

解决方案就是不使用用二进制,而是使用十进制(BigInteger)+小数点位置(scale)来表示小数,所有的十进制数字都可以用这种形式来表示,比如 0.1  =1*10^-1      scale=1

BigDecimal 运算分成两部分  ,BigInteger部分和更新scale即可,具体有兴趣的同学可以翻翻BigDecimal 的源码,在这里不再展开了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。