当前位置: 首页 > news >正文

Java 大厂面试高频坑:double 为什么不能精确保存 0.1?

在 Java 中,double是非常常用的小数类型。很多初学者会认为:

doubled=0.1;

这句代码的意思就是把0.1精确保存到了变量d中。

但严格来说,double 不能精确保存 0.1,它保存的只是一个非常接近 0.1 的近似值

这也是为什么在 Java 中经常会看到这样的现象:

publicclassTest{publicstaticvoidmain(String[]args){System.out.println(0.1+0.2);}}

输出结果可能是:

0.30000000000000004

而不是我们数学上认为的:

0.3

那么,为什么会这样呢?

一、double 是二进制浮点数

Java 中的double是双精度浮点数,底层遵循 IEEE 754 浮点数标准,使用二进制形式保存小数。

也就是说,计算机不是直接用十进制保存0.10.20.3,而是先把它们转换成二进制,再保存到内存中。

整数通常比较好理解,例如:

十进制 10 = 二进制 1010

但是小数就不一定能被二进制精确表示。

二、0.1 转成二进制是无限循环小数

我们在十进制中经常遇到除不尽的情况,例如:

1 / 3 = 0.333333333...

这里的0.333333...是无限循环小数。因为十进制无法用有限位数精确表示三分之一。

类似地,十进制的0.1转成二进制时,也会变成无限循环小数,大概是:

0.1(十进制) = 0.0001100110011001100110011...(二进制)

后面的0011会不断循环下去。

但是double的存储空间是有限的。double一般占 64 位,不可能保存无限长的二进制小数。所以它只能截断或四舍五入,保存一个近似值。

因此:

doubled=0.1;

这句代码并不是把精确的0.1存进去,而是把最接近0.1的某个二进制浮点数存进去。

三、为什么 0.1 + 0.2 不是 0.3?

因为0.10.2本身都不能被double精确表示。

可以理解为:

0.1 实际保存的是接近 0.1 的近似值 0.2 实际保存的是接近 0.2 的近似值 0.3 实际保存的是接近 0.3 的近似值

当执行:

System.out.println(0.1+0.2);

计算机拿到的并不是数学意义上完全精确的0.10.2,而是两个近似值。两个近似值相加后,结果可能就会出现非常小的误差。

所以输出结果可能是:

0.30000000000000004

这并不是 Java 算错了,而是浮点数表示方式决定的。

再看一个例子:

publicclassTest{publicstaticvoidmain(String[]args){System.out.println(0.1+0.2==0.3);}}

输出结果通常是:

false

原因是:

0.1 + 0.2 得到的近似值 和 0.3 保存的近似值 并不完全相等

所以,在 Java 中不要直接用==比较两个浮点数是否相等。

四、那为什么 System.out.println(0.1) 看起来正常?

你可能会问:

System.out.println(0.1);

输出明明是:

0.1

这不是说明double可以保存0.1吗?

其实不是。

这是因为 Java 在输出double时,会进行格式化处理。它会选择一个最简洁、最接近原值的十进制显示形式。所以虽然底层保存的是近似值,但打印时可能仍然显示为0.1

换句话说:

显示出来是 0.1 不代表底层精确保存了 0.1

只有在参与计算、比较或者格式化到更多小数位时,误差才更容易暴露出来。

五、浮点数应该怎么比较?

既然浮点数不能直接用==比较,那应该怎么判断两个浮点数是否相等呢?

常见做法是设置一个允许误差范围,也叫 epsilon。

例如:

publicclassTest{publicstaticvoidmain(String[]args){doublea=0.1+0.2;doubleb=0.3;if(Math.abs(a-b)<1e-10){System.out.println("近似相等");}else{System.out.println("不相等");}}}

这里的意思是:只要两个数的差值非常小,就认为它们相等。

这适合普通科学计算、统计计算、图形计算等场景。

六、金额计算不要用 double

如果是金额、余额、订单价格、支付结算等场景,不建议使用doublefloat

例如:

doubleprice=0.1;doubletotal=price*3;System.out.println(total);

结果可能不是你期望的精确金额。

在金融、支付、电商等业务中,金额必须精确,不能出现浮点误差。因此应该使用BigDecimal

例如:

importjava.math.BigDecimal;publicclassTest{publicstaticvoidmain(String[]args){BigDecimala=newBigDecimal("0.1");BigDecimalb=newBigDecimal("0.2");System.out.println(a.add(b));}}

输出:

0.3

注意,创建BigDecimal时推荐使用字符串:

newBigDecimal("0.1")

不推荐使用:

newBigDecimal(0.1)

因为0.1作为double传进去时,已经是近似值了。

七、面试怎么回答?

如果面试官问:double能不能精确保存0.1

可以这样回答:

严格来说,double 不能精确保存 0.1。因为 double 底层使用二进制浮点数表示小数,而十进制的 0.1 转成二进制是无限循环小数,double 的位数有限,只能保存一个近似值。所以浮点数计算可能出现精度误差,比如 0.1 + 0.2 可能得到 0.30000000000000004。普通浮点比较一般使用误差范围判断,涉及金额计算时应该使用 BigDecimal,并且推荐用字符串构造 BigDecimal。

八、总结

double不能精确保存0.1,根本原因是:十进制小数 0.1 转成二进制后是无限循环小数,而 double 的存储位数有限,只能保存近似值

因此,在 Java 中需要注意:

1. double 保存小数可能有精度误差 2. 0.1 + 0.2 不一定等于 0.3 3. 不建议直接用 == 比较浮点数 4. 普通计算可以使用误差范围比较 5. 金额计算应该使用 BigDecimal 6. BigDecimal 推荐使用字符串构造

一句话总结:

double 适合近似计算,不适合精确金额计算。
http://www.jsqmd.com/news/1152976/

相关文章:

  • 数据要素数据资产解决方案(PPT)
  • 安利一个JSON Path可视化查找神器
  • 搭建个智能体,这不是有手就会??
  • python+requests+excel 接口测试详解
  • Cap:开源版的 Loom,录屏分享这件事它包了
  • 从单兵到团队:用AI智能体协作框架构建高效自动化工作流
  • 深入JVM调优实战:从内存模型到GC优化,让你的Java应用性能飙升
  • 中山灯具厂出海布局遇阻,一份出海战略规划如何扭转困局
  • Mermaid:用 Markdown 写图表,文档告别画图死
  • 鸿蒙新特性:Radio 单项选择组件——构建健康评估问卷
  • kconfigDetector错误类型详解:从type error到unmet dependence的完整解析
  • 冒泡排序:从经典到大数据,一文掌握多种解法与演进
  • 高光谱拼接算法(一)扫推式成像和航带拼接算法
  • okbiye 一站式毕业论文 AI 写作板块,解决毕业生全流程论文撰写难题
  • 计算机毕业设计之旅行社网站的设计与实现
  • 2026年写小说软件排行榜:实测7款ai写小说工具,小说软件生成器哪家强?
  • 手机号码定位查询系统:3步实现号码归属地精准查询的完整指南
  • Linux字符设备驱动开发实战:从Hello World到完整驱动框架
  • B站视频下载神器:3分钟掌握BiliDownloader的完整使用指南
  • H2database 2.1.210 以下版本漏洞剖析:从JNDI注入到CVE-2022-23221的完整攻击链
  • 开源电子课本解析工具:如何轻松获取国家中小学智慧教育平台PDF教材
  • 终极指南:高效获取国家中小学智慧教育平台电子课本的完整解决方案
  • TMC7300与PIC18F86J15构建的高效有刷直流电机控制方案
  • 考个NPDP证书要花多少钱?这笔账,算清楚再报名!
  • 导航树有没有在表单的“表单加载后”事件中加载导航树第一个节点的API
  • 3步免费获取国家中小学智慧教育平台电子课本PDF:教师必备的终极下载指南
  • Scikit-learn K-Means 实战:3 个关键参数调优与 2 种评估指标对比
  • 从软件到认知:WSaiOS智能操作系统的架构范式革命
  • RTSP 拉流与录制:IPC 摄像头本地录像完整方案
  • SRWE窗口编辑器完整指南:如何突破Windows窗口限制的终极解决方案