热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java0.13==0.3_0.1+0.2===0.3?事情并没有那么简单!

java0.130.3_0.1+0.20.3?事情并没有那么简单!,Go语言社区,Golang程序员人脉社


前言


众所周知Javascript中0.1 + 0.2是不等于0.3的,这非常容易求证。如下图,在chrome控制台中显示2f97263b77f5b1a68ffdcf47480671e5.png


而且这似乎不是Javascript的问题。


在java中,输入0.1 + 0.2也是这个数,如下图:。4d6fd71a5336888cc5f6daed674c573c.png


那问题来了:为什么计算机计算0.1+0.2会不等于0.3呢?


浮点数的存储方式


想要弄清楚这个问题,首先得清楚浮点型在计算机中是如何存储的。


1、浮点数转换成二进制,并采用科学计数法表示。


2、浮点型的存储实现是按照IEEE754标准的,可分为两种:


单精度--32位


双精度--64位


单精度浮点型存储


单精度浮点型存储,举个例子:


在十进制中,0.75用科学计数法可以表示为7.5 * 10^-1,同样在二进制中,0.75可以表示为:


0.75 = 1.1 * 2^-1


即:


0.75 = (1 * 2^0 + 1 * 2^-1)* 2 ^-1


其中幂次方-1用阶码表示,而基数1.1由于二进制整数部分都是1,所以去掉1留下0.1作为尾数部分(因为都是1点多的形式,所以没有必要存放1)。因此0.75在单精度浮点数是这样表示的:afab23a9b0536da304bc0d25f9fab70f.png


阶码要加上一个基数,这个基数为2^(n-1) -1,n为阶码的位数,32位的阶码位是8位,所以这个基数为127,8位阶码能表示的最小整数位0,最大整数位255,所以能表示的指数范围为:-127~128,上面要表示的指数为-1,需要加上基数127,就变成126,如上图所示。


而尾数为0.1,所以尾数的最高位为1,后面的值填充0。


反过来,如果知道一个二进制的存储方式,同样地可以转换成10进制,如上结果为:


(1 + 1 * 2^-1) * 2^(126 - 127) = 1.5 * 2^-1 = 0.75


那么按照上面的理论,在二进制中0.1又该如何表示呢?


0.1无法被表示为这种方式,就像1/3无法在十进制中精确表示一样,在二进制中只能是用一个数尽可能的接近0.1。


双精度浮点型存储


Javascript的Number类型使用的是双精度浮点型,也就是C语言中的double类型。


因为C语言能够读取原始的内存信息,所以用C语言看看在双精度浮点型中0.1存储成什么样。(代码来自这Is there a printf converter to print in binary format?)


#include


void printBits(size_t const size, void const * const ptr)


{


unsigned char *b = (unsigned char*) ptr;


unsigned char byte;


int i, j;


for (i=size-1;i>=0;i--)


{


for (j=7;j>=0;j--)


{


byte = (b[i] >> j) & 1;


printf("%u", byte);


}


}


puts("");


}


int main (void)


{


double a = 0.1;


printBits(sizeof(a), &a);


return 0;


}



结果如下图所示:320b8c98f53441993a31427b1c493e49.png


双精度浮点数用11位表示阶码,52位表示尾数,如图所示40ded1bc5c87ad30e40bdaa638437eb4.png


所以双精度的阶码基数为2^10 - 1 = 1023,0.1的阶码为01111111011,等于十进制1019,所以它的指数为-4,尾数约等于0.6afd8b75b78a4ebd5c863e74e8cd117d3.png


有了这个尾数再乘上指数,如图所示756a320d99453c890d6182eb631c1fe1.png


也就是说0.1的实际存储要比0.1大。


0.2和0.1的区别在于0.2比0.1的阶码大了1,其他完全一样,也就是说0.2的实际存储也是偏大的。


所以0.1 + 0.2是大于0.3!


解决方法


解决方法有很多种,比如mathjs库、decimal.js等。这些库都很好的解决这个问题。


但如果只是涉及到比较简单的浮点型相加而去引用第三方库,无疑是用大炮打蚊子。


toFixed


在Javascript原生方法中提供了一个方法:Number.prototype.toFixed()


toFixed()方法使用定点表示法来格式化一个数


语法如下:


numObj.toFixed(digits)



其中参数digits是小数点后数字的个数:介于0到20之间。


返回的是一个数值的字符串形式,所以需要将结果强制转换为浮点型。


parseFloat((0.1 + 0.2).toFixed(10))//结果为0.3


parseFloat((0.3 / 0.1).toFixed(10)) // 结果为 3


parseFloat((0.7 * 180).toFixed(10))//结果为126


parseFloat((1.0 - 0.9).toFixed(10)) // 结果为 0.1


parseFloat((9.7 * 100).toFixed(10)) // 结果为 970


parseFloat((2.22 + 0.1).toFixed(10)) // 结果为 2.32



总结


计算机中使用IEEE754标准实现的浮点型存储都会有这个问题:用二进制来存储小数,而大部分小数转成二进制之后都是无限循环的值,因此存在取舍问题,也就是精度丢失。从而使得0.1 + 0.2 !== 0.3。


结尾


更多文章请移步楼主github,如果喜欢请点一下star,对作者也是一种鼓励。


b739ec46bb5c46d9c0aa4ce35ba1ea56.png


关于找一找教程网


本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。


本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。


[0.1+0.2===0.3?事情并没有那么简单!]http://www.zyiz.net/tech/detail-139750.html





推荐阅读
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有