今天我们来解决0.1d+0.2d==0.3d是false的问题!

简介: 今天我们来解决0.1d+0.2d==0.3d是false的问题!
之前文章给大家做了一些测试题,如果全对的话,说明你底子很厚,如果有出错的话也很正常,毕竟谁都有遗漏的知识,谁都有忘记的,回头再看一遍让自己记住就可以了,没有什么可气馁的(这是偷偷地说给我自己听得,哈哈) 正如标题一样,下面的公式为什么是false
double a = 0.1d;
double b = 0.2d;
double c = 0.3d;
System.out.println((a + b) == c);
System.out.println((0.1d + 0.2d) == 0.3d);
要了解这个,我么先的了解了解IEEE 754,何为IEEE 754呢? IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。(百度百科,在写这个文章之前,鬼知道什么是IEEE 754但是,现在我知道了。而且为了了解这个我竟然想了好几天)_地址:http://www.softelectro.ru/ieee754_en.html_不论怎么样,计算机存储都是二进制的,所以要想看下去,就必须知道知道十进制转二进制,整数转二进制除以2求余,小数转二进制正好相反小数乘以2取整,例如IEEE 754例子一样155.625转成二进制是什么呢?

先算155


155 -> 155 / 2 = 77 --- 1
77 -> 77 / 2 = 38 --- 1
38 -> 38 / 2 = 19 --- 0
19 -> 19 / 2 = 9 --- 1
9 -> 9 / 2 = 4 --- 0
4 -> 4 / 2 = 2 --- 0
2 -> 2 / 2 = 1 --- 0
1 -> 1 / 2 = 0 --- 1
155 = 1 * 2^7 + 0 * 2^6 + 0 * 2^5 + 0 * 2^4 + 1 * 2^3 + 0*2 ^ 2 + 1 * 2^1 + 1 * 2^0  
所以二进制是 10001011

在算0.625

0.625 -> 0.625 * 2 = 1.25 --- 1
1.25 -> 0.25 * 2 = 0.5 --- 0
0.5 -> 0.5 * 2 = 1.0 --- 1
0.625 = 1 * 2^-1 + 0 * 2^-2 + 1 * 2^-3
所以二进制是0.101

所以

155.625十进制等于1001101.101二进制
1.55625 * 10 ^2 = 1.001101101 * 2^111(二进制) = 1.001101101 * 2^7

640.png
这个图看懂了吗?我感觉一篇文章说不完。。。

浮点数类型

640.jpg

单/双精度浮点数分别对应java语言中的float和double

单精度浮点数

640.jpg

双精度浮点数

640.jpg
S代表符号位,0代表正,1代表负
E代表指数,b代表指数有多少位
M为尾数,n代表尾数有多少位

现在我们回归正题

0.1 转成计算机语言应该是多少呢?

0.1 -> 0.1 * 2 = 0.2 取 0
0.2 -> 0.2 * 2 = 0.4 取 0
0.4 -> 0.4 * 2 = 0.8 取 0 
0.8 -> 0.8 * 2 = 1.6 取 1
0.6 -> 0.6 * 2 = 1.2 取 1
0.2 -> 0.2 * 2 = 0.4 取 0
0.4 -> 0.4 * 2 = 0.8 取 0 
0.8 -> 0.8 * 2 = 1.6 取 1
可以看出来循环
得到的结果是
0 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011...
二进制0舍去,但是这是小数又因为保留52位(这里来说double)所以得到
1.1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011  010 * 2^-4
(因为无限循环,又因逢1进位所以)
这把用计算机储存标识应该是什么呢
F = (-1)^0 * 2^(-1020) * (1 + 1 0011 0011 0011 .../2^54)

计算只看尾数所以
0.1 = 1.1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011  010 * 2^-4

0.2雷同得到

0.2 = 1.1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011  010 * 2^-3

0.1+0.2

0.1 + 0.2 = 
        1.1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 * 2 ^ -4 +
       11.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010 * 2 ^ -4
    = 100.1100 1100 1100 1100 1100 1100 1100 1100 1100 1110 1100 1100 1110 * 2 ^ -4   
    =   1.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 (10)  * 2 ^ -2
    =   1.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0100  * 2 ^ -2

0.3雷同得到

0.3 = 1.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011  * 2 ^ -2

0.1 + 0.2和0.3比较

1.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0100  * 2 ^ -2
1.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011  * 2 ^ -2

可以看出他们相差了

0.0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001  * 2 ^ -2

这不是2^-54吗?来我们验证下


public static void main(String[] args) {
    double a = 0.1d;
    double b = 0.2d;
    double c = 0.3d;
    System.out.println((a + b) == c);
    System.out.println((a + b) == (c + Math.pow(2, -54)));
}
false
true
学到了吗?当然我讲的只是皮毛毕竟,连接文中我已发,还是靠自己去理解才能真的记忆住,此文只是开篇,更多的东西靠自己去得到。 既然说到这个了如果我在程序中以及用到double和float怎么办?第一种看看有没有所运算,没有做运算double类型用==,或Double.compare(double a,double b)。Double用a.equals(b)或Objects.equals(a,b),如果做运算,只能退而求其次,对于不在乎精确度的项目,Math.abs(a-b)
相关文章
Layui 内置方法 - layer.close(关闭特定层)
Layui 内置方法 - layer.close(关闭特定层)
709 0
|
JavaScript 前端开发
VUE组件:如何在Vue中实现组件的动态引入?
VUE组件:如何在Vue中实现组件的动态引入?
2117 0
|
NoSQL API 数据库
【Cassandra】使用Docker部署Cassandra集群
Cassandra是一个开源分布式NoSQL数据库系统。
2567 0
|
JavaScript 前端开发 jenkins
【前端】vue项目打包Browserslist: caniuse-lite is outdated. Please run: npx update-browserslist-db@latest解决方案
【前端】vue项目打包Browserslist: caniuse-lite is outdated. Please run: npx update-browserslist-db@latest解决方案
2950 0
|
11月前
|
机器学习/深度学习 人工智能 算法
整合海量公共数据,谷歌开源AI统计学专家DataGemma
【10月更文挑战第28天】谷歌近期开源了DataGemma,一款AI统计学专家工具,旨在帮助用户轻松整合和利用海量公共数据。DataGemma不仅提供便捷的数据访问和处理功能,还具备强大的数据分析能力,支持描述性统计、回归分析和聚类分析等。其开源性质和广泛的数据来源使其成为AI研究和应用的重要工具,有助于加速研究进展和推动数据共享。
259 6
|
11月前
|
存储 安全 API
利用环境变量管理配置:最佳实践与技巧
本文介绍了如何利用环境变量管理应用程序配置,涵盖安全性、灵活性和简化部署等方面的优势。详细探讨了最佳实践,包括避免敏感信息泄露、使用`.env`文件、环境特定配置、环境变量注入与验证,以及使用第三方服务。同时分享了一些实用技巧,如分层管理、环境变量加密和版本控制。旨在帮助开发者更高效、安全地管理应用配置。
|
安全 Java 测试技术
解密Java并发中的秘密武器:LongAdder与Atomic类型
解密Java并发中的秘密武器:LongAdder与Atomic类型
510 1
|
Web App开发 监控 JavaScript
一些常用的 Vue 性能分析工具
【10月更文挑战第2天】
705 1
|
网络协议 算法 安全
【网络协议基础】TCP/IP协议大全
TCP/IP协议是现代计算机网络通信的基础,是互联网及局域网广泛使用的一套协议。TCP/IP协议集采用分层模型,以便于网络的设计、实现和管理。
803 2
|
缓存 API 开发工具
Unity——工程与资源
Unity——工程与资源
591 1