前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Double为什么会丢失精度

Double为什么会丢失精度

作者头像
用户4283147
发布于 2022-10-08 04:40:00
发布于 2022-10-08 04:40:00
2.4K00
代码可运行
举报
文章被收录于专栏:对线JAVA面试对线JAVA面试
运行总次数:0
代码可运行

在工作中,谈到有小数点的加减乘除都会想到用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。

如遇到

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
输出是 0.19999999999999998
double类型 0.3-0.1的情况。需要将0.3转成二进制在运算
0.3 * 2 = 0.6 => .0 (.6)00.6
0.6 * 2 = 1.2 => .01 (.2)10.2
0.2 * 2 = 0.4 => .010 (.4)00.4
0.4 * 2 = 0.8 => .0100 (.8)00.8
0.8 * 2 = 1.6 => .01001 (.6)10.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.总结

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 (1)商业计算使用BigDecimal。
 (2)尽量使用参数类型为String的构造函数。
 (3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。
 (4)我们往往容易忽略JDK底层的一些实现细节,导致出现错误,需要多加注意。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 对线JAVA面试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JavaScript 浮点数之迷:0.1 + 0.2 为什么不等于 0.3?
“0.1 + 0.2 = ?” 这个问题,你要是问小学生,他也许会立马告诉你 0.3。但是在计算机的世界里就没有这么简单了,做为一名程序开发者在你面试时如果有人这样问你,小心陷阱喽! 你可能在哪里见过
五月君
2020/02/19
4.1K0
浮点数的坑很深,但不多
大家好,我是扔物线朱凯。刚才那个 0.1 + 0.2 不等于 0.3 的情况是真实存在的,不信你可以亲自试一下。我用的是 Kotlin,你换成 Java、JavaScript、Python、Swift 也都是这样的结果。要解决它也简单,在数值的右边加个 f,把它从双精度改成单精度的就可以了:
扔物线
2024/01/01
3420
浮点数的坑很深,但不多
《Java从入门到失业》第三章:基础语法及基本程序结构(3.7):运算符(小数二进制、科学记数法、IEEE754标准)
       要讨论浮点数运算,牵涉到的知识比较多,下面一点一点的来逐步展开。为了便于同时讨论十进制和二进制数,我们做一个约定,我们把十进制数简写为N10,把二进制数简写为N2。
用户7801119
2020/09/27
8300
浮点数精度问题透析:小数计算不准确+浮点数精度丢失根源
之前自己答的不是满意(对 陈嘉栋的回答 还是满意的),想对这个问题做个深入浅出的总结
周陆军
2019/05/17
3K0
浮点数原理与精度损失问题
计算机中小数的表示按照小数点的位置是否固定可以分为浮点数和定点数。为了方便和float32浮点数做对比,我们构造一个32位精度的定点数,其中小数点固定在23bit处:
TOMOCAT
2020/10/29
3.2K0
浮点数原理与精度损失问题
float与double的范围和精度
单精度浮点数在机内占4个字节,用32位二进制描述。 双精度浮点数在机内占8个字节,用64位二进制描述。 浮点数在机内用指数型式表示,分解为:数符,尾数,指数符,指数四部分。 数符占1位二进制,表示数的正负。 指数符占1位二进制,表示指数的正负。 尾数表示浮点数有效数字,0.xxxxxxx,但不存开头的0和点 指数存指数的有效数字。 指数占多少位,尾数占多少位,由计算机系统决定。 可能是数符加尾数占24位,指数符加指数占8位 -- float. 数符加尾数占48位,指数符加指数占16位 -- double.
week
2018/08/24
28.9K0
什么是浮点数?
简单回顾一下,简单来说,用定点数表示数字时,会约定小数点的位置固定不变,整数部分和小数部分分别转换为二进制,就是定点数的结果。
_Kaito
2021/03/23
1.4K0
什么是浮点数?
double转bigDecimal精度问题
double转bigDecimal精度问题 需要用到bigDecimal的字符串构造来转
全栈程序员站长
2022/09/05
2.1K0
浮点数在计算机中的精度问题
不论大家使用的是什么编程语言想必都知道浮点数在计算机中存在一定的精度问题,特别是有float类型的编程语言中,大部分编程都是建议直接使用更高精度的double类型。下面我做的示例都以python为主。
不知名小白
2025/01/09
1250
连科班生都少有关注的【浮点数】问题!
但用定点数表示小数时,存在数值范围、精度范围有限的缺点,所以在计算机中,我们一般使用「浮点数」来表示小数。
程序视点
2023/12/06
3440
连科班生都少有关注的【浮点数】问题!
JavaScript 中 0.1 + 0.2 的精度以及数字类型的整理
JavaScript 中的所有数字都是浮点数,使用 64 位二进制来表示,也叫做双精度浮点型,这种方式出自于 IEEE-754 标准。
上山打老虎了
2022/06/15
7530
JavaScript 中 0.1 + 0.2 的精度以及数字类型的整理
整数、浮点数在计算机中的存储
  计算机要处理的信息是多种多样的,如数字、文字、符号、图形、音频、视频等,这些信息在人们的眼里是不同的。但对于计算机来说,它们在内存中都是一样的,都是以二进制的形式来表示。要想学习编程,就必须了解二进制,它是计算机处理数据的基础。
mukekeheart
2019/09/29
1.8K0
整数、浮点数在计算机中的存储
计算误差的真相:为什么 float 加法会出现精度损失?
float(浮点数)是一种在计算机编程中常用的数据类型,它用于表示带小数点的数字。在大多数编程语言中,float类型通常使用32位来表示,也被称为“单精度浮点数”或“单精度实数”。它可以表示的数值范围比整数类型要大得多,并且可以存储小数位数较多的数值。在C++、Java等语言中,float类型的定义通常如下:
Lion Long
2024/07/31
1K0
计算误差的真相:为什么 float 加法会出现精度损失?
IEEE 754标准--维基百科
IEEE二进制浮点数算术标准(IEEE 754) 是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number),一些特殊数值((无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。
风骨散人Chiam
2021/09/06
1.7K0
Java Double转Bigdecimal丢失精度原因学习
注意事项: 不能直接使用Bigdecimal的构造函数传double进行转换,部分数值会丢失精度,因为计算机是二进制的Double无法精确的储存一些小数位,0.1的double数据存储的值实际上并不真的等于0.1 如该方式将0.1转换为Bigdecimal得到的结果是 0.1000000000000000055511151231257827021181583404541015625
全栈程序员站长
2022/09/05
3.8K0
【小家java】Java数值运算 [加减乘除] 精度丢失原因分析,提供保证精度的MathHelper工具类
有没有一种触目惊心的感觉,感觉回去检查检查自己的代码,有没有一些数值运算吧,哈哈。这个问题相当严重,比如你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。
YourBatman
2019/09/03
1.8K0
js浮点数精度问题详解
浮点数精度问题是指在计算机中使用二进制表示浮点数时,由于二进制无法精确表示某些十进制小数,导致计算结果可能存在舍入误差或不精确的情况。
can4hou6joeng4
2023/11/17
6910
小朋友学C语言(43):浮点数的深入分析
IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷∞与非数值NaN),以及这些数值的“浮点数运算符”。 IEEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)。只有32位模式有强制要求,其他都是选择性的。大部分编程语言都有提供IEEE浮点数格式与算术,但有些将其列为非必需的。例如,IEEE 754问世之前就有的C语言,现在有包括IEEE算术,但不算作强制要求 C语言的float通常是指IEEE单精确度,而double是指双精确度。
海天一树
2019/03/06
2K0
小朋友学C语言(43):浮点数的深入分析
计算机如何存储浮点数和定点数?
简单加法在js算出结果居然不是准确的0.9,而是0.8999999999999999,why?
JavaEdge
2023/01/11
1.3K0
计算机如何存储浮点数和定点数?
小小的 float,藏着大大的学问
十进制转换二进制的方法相信大家都熟能生巧了,如果你说你还不知道,我觉得你还是太谦虚,可能你只是忘记了,即使你真的忘记了,不怕,贴心的小林在和你一起回忆一下。
小林coding
2020/12/11
1.8K0
小小的 float,藏着大大的学问
推荐阅读
相关推荐
JavaScript 浮点数之迷:0.1 + 0.2 为什么不等于 0.3?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档