为什么0.2 + 0.1 ≠ 0.3:一文彻底搞懂浮点数float和double

Java的基本数据类型中,有一类“不靠谱”的数据类型:浮点数。

先来领教下浮点数的不靠谱,对于下面的计算题:

0.2 + 0.1 =

对于这样一个小学三年级的学生100%的能给出正确答案的题目,看看计算机给出的答案是多少:

在这里插入图片描述
如图,答案是0.30000000000000004

在这里插入图片描述

这究竟是怎么一回事呢?

本文将彻底解开这个疑团。

一、浮点数的存储原理

1,什么是浮点数

顾名思义,浮点数就是小数点可以浮动的数,是计算机表示数据的方式。

要了解浮点数,先回忆下科学计数法,数字3140,可以有如下多种表示方法:

在这里插入图片描述

同一个数,有不同的表示方法,区别在于小数点的位置不一样,整数和小数部分是可变的,指数也随之改变,非常灵活。

浮点数就是用二进制的科学计数法表示的数字。

以小数112.5为例,用十进制科学计数法可以表示为:

在这里插入图片描述

计算机是如何用浮点数存储这个数字的呢?

  • 先将这个数转换为二进制表示的数字:

在这里插入图片描述

  • 将这个二进制1110000.1用科学计数法表示:
    在这里插入图片描述
  • 计算机将其化为三部分进行存储:
  • 阶码E(如上图的指数6)
  • 尾数M(如上图的小数点后面的1100001)
  • 符号位S(正数为0,负数为1)

在这里插入图片描述

在这里插入图片描述

单精度浮点数的存储格式为:

  • S=0
  • E=10000101
  • M=1100001

在这里插入图片描述

双精度浮点数的存储格式为:

  • S=0
  • E=10000000101
  • M=1100001
    在这里插入图片描述

细心的同学会发现两个细节:

  • ①单精度存储的阶码10000101133,不是6,原因是单精度的阶码是在指数的基础上加了偏移量127。同理,双精度的阶码也会加上偏移量,不过偏移量是1023
  • ②有效数字的整数1不会被存储,即1.1100001的整数部分不会被存储,只会存储小数部分,这是因为所有浮点数的整数部分都是1,可以省略,但在运算过程中会加上

2,精度损失

由上可知,单精度最多23位尾数,双精度最多52位尾数,无法精确表示所有实数,对于尾数超出23位或者52位的二进制数,必须进行舍入处理。这一步骤就引入了精度损失的源头。

到这里,就搞清楚了浮点数在计算机中的存储格式,我们可能还有几个疑问:
1,为什么阶码要加上偏移量127或1023呢
2,有小数点位置可变的浮点数,有没有小数点位置不变的定点数呢,
3,单精度和双精度的单、双是怎么来的呢
4,既然浮点数会损失精度,为什么计算机还要采用这种表示方法呢

答案见另一篇博文

二、0.2 + 0.1 ≠ 0.3 的根本原因

2.1 二进制表示的挑战

在十进制系统中,0.10.20.3都是简单的小数,但在二进制系统中,情况截然不同。以0.1为例,它在二进制中是一个无限循环小数,循环体是0011

在这里插入图片描述

由于floatdouble的尾数位有限,无法完全存储这个无限循环,因此0.1在内存中会被近似存储为一个接近但不完全等于0.1的二进制数。

2.2 加法过程中的误差累积

当进行0.2 + 0.1的运算时,实际上是在对两个近似值进行相加。由于每个数都已经经过了舍入处理,它们的和自然也是一个近似值。更具体地说,这两个数在二进制下的近似表示相加后,并不会正好得到0.3的精确二进制表示,而是接近但不等于预期的结果。

2.3 显示问题

此外,当这个近似和被转换回十进制显示给用户时,由于通常的四舍五入规则,最终结果显示为一个看似不符合数学直觉的值,而非直观的0.3

三、解决与应对策略

3.1 使用BigDecimal

对于需要高精度计算的场景,如财务计算,Java提供了BigDecimal类来处理浮点数。BigDecimal可以精确表示任意精度的十进制数,避免了浮点运算的精度损失。

3.2 精度意识

开发者应具备浮点数精度的意识,理解哪些场景下可以接受近似计算,哪些场景下必须使用精确计算。在进行比较时,应避免直接使用==操作符,而应使用一个很小的容差值来进行近似比较。

3.3 数学库的选择

在某些高级语言或库中,提供了更高精度的数学运算函数,这些函数在内部使用更复杂的算法来减少误差,提高计算的准确性。

四、结语

理解floatdouble的精度问题,不仅能够帮助我们避免常见的编程陷阱,也是深入计算机科学底层原理的一次探索。记住,浮点数的不精确并非缺陷,而是硬件与软件设计上的一种权衡——牺牲了一定的精确性,换取了计算速度和存储空间上的效率。作为开发者,关键在于何时何地选择正确的工具,以及如何合理地管理和应对可能出现的精度误差。希望通过本文的介绍,你能够对浮点数的存储与运算有了更深刻的理解,并能在实际开发中做出更明智的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小手追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值