01等于0是为什么,java面试官:Double为什么会丢失精度?解决方法?答出给1万月薪

在工作中,谈到有小数点的加减乘除都会想到用BigDecimal来解决,但是有很多人对于double或者float为啥会丢失精度一脸茫然。还有BigDecimal是怎么解决的?话不多说,我们开始。

1.浮点数是啥?

浮点数是计算机用来表示小数的一种数据类型,采用科学计数法。在java中,double是双精度,64位,浮点数,默认是0.0d。float是单精度,32位.浮点数,默认是0.0f;

在内存中存储

float 符号位(1bit) 指数(8 bit) 尾数(23 bit)

double 符号位(1bit) 指数(11 bit) 尾数(52 bit)

float在内存中占8位,由于阶码实际存储的是指数的移码,假设指数的真值是e,阶码为E,则有E=e+(2^n-1 -1)。其中 2^n-1 -1是IEEE754标准规定的指数偏移量,根据这个公式我们可以得到 2^8 -1=127。于是,float的指数范围为-128 +127,而double的指数范围为-1024 +1023。其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。

loat的范围为-2^128 ~ +2^127,也即-3.40E+38 ~ +3.40E+38;

double的范围为-2^1024 ~ +2^1023,也即-1.79E+308 ~ +1.79E+308

2.走进失真之科学计数法

我们先说说科学计数法,科学计数法是一种简化计数的方法,用来近似表示一个极大或极小且位数较多的数,对于位数较小的数值,科学计数法没有什么优势,但对于位数较多的数值其计数方法的优势就非常明显了。例如:光的速速是300000000米/秒,全世界人口数大约是6100000000。类似光的速度和世界人口数这样大数值的数,读、写都很不方便,所以光的速度可以写成3*10^8,全世界人口数可以写成6.1*10^9。所以计算器用科学计数法表示光速是3E8,世界人口数大约是6.1E9。

我们小时候玩计算器喜欢疯狂的累加或者累减,到最后计算器就会显示下图。这个就是科学计数法显示的结果

那图中真实的值是 -4.86*10^11=-486000000000。十进制科学计数法要求有效数字的整数部分必须在【1,9】区间内。

3.走进失真之精度

计算机在处理数据都涉及到数据的转换和各种复杂运算,比如,不同单位换算,不同进制(如二进制十进制)换算等,很多除法运算不能除尽,比如10÷3=3.3333.....无穷无尽,而精度是有限的,3.3333333x3并不等于10,经过复杂的处理后得到的十进制数据并不精确,精度越高越精确。float和double的精度是由尾数的位数来决定的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。float:2^23 = 8388608,一共七位,由于最左为1的一位省略了,这意味着最多能表示8位数:28388608 = 16777216 。有8位有效数字,但绝对能保证的为7位,也即float的精度为7~8位有效数字;double:2^52 = 4503599627370496,一共16位,同理,double的精度为16~17位。

当到达一定值自动开始使用科学计数法,并保留相关精度的有效数字,所以结果是个近似数,并且指数为整数。在十进制中小数有些是无法完整用二进制表示的。所以只能用有限位来表示,从而在存储时可能就会有误差。对于十进制的小数转换成二进制采用乘2取整法进行计算,取掉整数部分后,剩下的小数继续乘以2,直到小数部分全为0。

如遇到

输出是 0.19999999999999998double类型 0.3-0.1的情况。需要将0.3转成二进制在运算0.3 * 2 = 0.6 => .0 (.6)取0剩0.60.6 * 2 = 1.2 => .01 (.2)取1剩0.20.2 * 2 = 0.4 => .010 (.4)取0剩0.40.4 * 2 = 0.8 => .0100 (.8) 取0剩0.80.8 * 2 = 1.6 => .01001 (.6)取1剩0.6.............

3.总结

从上面看,很清楚为什么浮点数有精度问题。简单地说,float和double类型主要是为科学计算和工程计算而设计的。它们执行二进制浮点运算,这些运算经过精心设计,能够在广泛的数值范围内提供更精确的快速近似和计算而精心设计的。但是,它们不能提供完全准确的结果,因此不能用于需要计算精确结果的场景中。当浮点数达到一定的大数时自动使用科学计数法。这样的表示只是近似真实数而不等于真实数。当十进制小数转换为二进制时,也会出现无限循环或超出浮点数尾部的长度。

4.那我们怎么用BigDecimal来解决?

大家看下面的两个输出

输出结果:

0.299999999999999988897769753748434595763683319091796875

0.3

图上阿里的代码约束插件在图表上已经标记了警告,所以让我使用String字符串参数的构造方法创建BigDecimal。由于double不能精确表示为0.3(任何有限长度的二进制),因此用double构造函数传递的值不完全等于0.3。使用bigdecimal时,必须使用String字符串参数构造方法来创建它。在这一点上,有没有好奇的疑问。BigDecimal原理是什么?为什么它就没事?原理很简单。BigDecimal是不可变的,可以用来表示任意精度的带符号十进制数。double的问题是从小数点转换到二进制丢失精度,二进制丢失精度。BigDecimal在处理的时候把十进制小数扩大N倍让它在整数上进行计算,并保留相应的精度信息。至于BigDecimal是怎么保存的可以翻阅一下源代码。

5.总结 (1)商业计算使用BigDecimal。 (2)尽量使用参数类型为String的构造函数。 (3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。 (4)我们往往容易忽略JDK底层的一些实现细节,导致出现错误,需要多加注意。

本文分享自微信公众号 - IT大咖说(itdakashuo)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

0的0次方等于多少(高数里0的0次方是0还是1)_小樱知识基点 - MBA智库百科为什么8的0次方=1? 8的0次方为什么等于一_8的零次方等于&#49你真的懂学习率了吗 - Welcome to AI WorldC语言中一个感叹号加一个字母代表什么意思? c语言中输入 ...矩阵a的平方为0一公分等于多少厘米?公分和厘米是同一个概念吗?_合抱木 ...0乘无穷型极限为什么不等于0_0乘∞什么时候等于0_为什么0 ...lne的x+等于多少Excel计算百分比时分母为0的处理【运筹学】0-1整数规划(隐枚举法)IP地址 子网掩码 网络号 主机号 网络地址 主机地址以及ip段/数字-如192 168 0 1/24是什么意思字符与字符串中的‘\0‘, ‘0‘, 0;strlen()函数求字符串长度计算方法(sizeof()验证)计算机中,为什么一个字节是8位?动态规划之背包问题——01背包关于0xa0贪心算法的一些例子(包括01背包问题)(并不懂,只是保留)类加载流程-01js中-1,0,1,2等数字判断true和false【牛客网】C/C++牛客网专项刷题(01C语言数组元素下标为何从0开始DW01+8205A保护电路详解计算与推断思维 十二、为什么均值重要合同变换为什么是一个行变换再跟一个相应的列变换?为什么一个字节是八个bitWin64 驱动内核编程-23.Ring0 InLineHook 和UnHook51单片机的工作寄存器R0~R7位于内部RAM什么位置背包问题01图文附带代码,非常清晰0/1背包问题(递归解决,递推解决)1bit等于多少字节,换算方法?《Windows黑客编程技术详解》之病毒启动技术创建进程API、突破SESSION0隔离、内存加载详解(3)CRC算法详解8205A 和DW01+为什么1个字节(byte)是8个比特(bit)MySQL中的分区是什么?为什么要分区?有什么好处?怎么进行分区?Unity Shader-深度相关知识总结与效果实现(LinearDepth,Reverse Z,世界坐标重建,软粒子,高度雾,运动...ASCII 、GB2312、GBK、UTF-8 编码|0x7f-0xffSTM32的推挽输出与开漏输出,及模拟IIC驱动MLX90615失败问题总结c3p0数据库连接池死锁问题和mysql重连,连接丢失
01的视力是多少度01视力表是对的多少使用yt大花紫薇花期大花紫薇修剪技巧080819阿普索夫01bz移动线路1移动线路2最新章节蟋蟀交易市场蟋蟀养殖前景051酒馆最弱男来了03要綱0280684全部小说07年联播038和05笔粗细效果图038和05的笔有什么区别0745天牢营救102沉睡的梦魇第二话梦魇之街在线樱花0791是哪个网贷催收020是哪里的区号啊019012是什么号码073期阿旺双色球预测奖号0770我们是一家人02495508是什么电话