热门标签 | 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





推荐阅读
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • 版本控制工具——Git常用操作(下)
    本文由云+社区发表作者:工程师小熊摘要:上一集我们一起入门学习了git的基本概念和git常用的操作,包括提交和同步代码、使用分支、出现代码冲突的解决办法、紧急保存现场和恢复 ... [详细]
  • 本文探讨了在 SQL Server 中使用 JDBC 插入数据时遇到的问题。通过详细分析代码和数据库配置,提供了解决方案并解释了潜在的原因。 ... [详细]
  • KMP算法是一种高效的字符串模式匹配算法,能够在不进行回溯的情况下完成匹配,其时间复杂度为O(m+n),其中m和n分别为文本串和模式串的长度。本文将详细介绍KMP算法的工作原理,并提供C语言实现。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • This post discusses an issue encountered while using the @name annotation in documentation generation, specifically regarding nested class processing and unexpected output. ... [详细]
  • ElasticSearch 集群监控与优化
    本文详细介绍了如何有效地监控 ElasticSearch 集群,涵盖了关键性能指标、集群健康状况、统计信息以及内存和垃圾回收的监控方法。 ... [详细]
  • 在编译BSP包过程中,遇到了一个与 'gets' 函数相关的编译错误。该问题通常发生在较新的编译环境中,由于 'gets' 函数已被弃用并视为安全漏洞。本文将详细介绍如何通过修改源代码和配置文件来解决这一问题。 ... [详细]
  • 深入解析 Android IPC 中的 Messenger 机制
    本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ... [详细]
  • 本文探讨了如何在Classic ASP中实现与PHP的hash_hmac('SHA256', $message, pack('H*', $secret))函数等效的哈希生成方法。通过分析不同实现方式及其产生的差异,提供了一种使用Microsoft .NET Framework的解决方案。 ... [详细]
  • 本文详细介绍了如何在Android 4.4及以上版本中配置WebView以实现内容的自动高度调整和屏幕适配,确保中文显示正常,并提供代码示例。 ... [详细]
  • 云屏系统基于嵌入式微系统msOS,旨在解决当前嵌入式彩屏GUI编程中硬件要求高、软件开发复杂、界面效果不佳等问题。该系统通过结合MCU和Android技术,利用Html5+JavaScript实现高效、易用的图形用户界面开发,使嵌入式开发人员能够专注于业务逻辑。 ... [详细]
  • 本文档汇总了Python编程的基础与高级面试题目,涵盖语言特性、数据结构、算法以及Web开发等多个方面,旨在帮助开发者全面掌握Python核心知识。 ... [详细]
  • 序列化与反序列化是数据处理中的重要技术,特别是在网络通信和数据存储中。它们允许将复杂的数据结构转换为可传输或存储的格式,再从这些格式恢复原始数据。本文探讨了序列化与反序列化的基本概念,以及它们在不同协议模型中的角色。 ... [详细]
  • 本文详细探讨了Java命令行参数的概念、使用方法及在实际编程中的应用,包括如何通过命令行传递参数给Java程序,以及如何在Java程序中解析这些参数。 ... [详细]
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社区 版权所有