Java 如何正确比较两个浮点数
看下面这段代码,将 d1 和 d2 两个浮点数进行比较,输出的结果会是什么?
double d1 = .1 * 3;
double d2 = .3;
System.out.println(d1 == d2);
按照正常逻辑来看,d1 经过计算之后的结果应该是 0.3,最后打印的结果应该是 true,对吧?但是运行一下就会发现结果并不是 true 而是 false。
输出一下 d1,发现得到的答案不是想象中的 0.3 而是 0.30000000000000004,所以和 d2 进行比较结果自然是 false。
如何正确地比较浮点数(单精度的 float 和双精度的 double),不单单是 Java 特定的问题,在计算机的内存中,存储浮点数时使用的是 IEEE 754 标准,就会有精度的问题。
存储和转换的过程中浮点数容易引起一些较小的舍入误差,正是这个原因,导致在比较浮点数的时候,不能使用“==”操作符——要求严格意义上的完全相等。
那么如何正确的比较浮点数呢?这里有两种方案。
方案1:比较绝对值误差
第一种方案是允许两个值之间存在一点误差(指定一个阈值),使用 Math.abs() 方法来计算两个浮点数之间差异的绝对值,如果这个差异在阈值范围之内,我们就认为两个浮点数是相等的。
final double THRESHOLD = .0001;double d1 = .1 * 3;
double d2 = .3;if (Math.abs(d1 - d2) < THRESHOLD) {System.out.println("d1 和 d2 相等");
} else {System.out.println("d1 和 d2 不相等");
}
Math.abs() 方法用来返回 double 的绝对值,如果 double 小于 0,则返回 double 的正值,否则返回 double。也就是说,abs() 后的结果绝对大于 0,如果结果小于阈值(THRESHOLD),我们就认为 d1 和 d2 相等。
方案2:BigDecimal
第二种方案是使用 BigDecimal 类,可以指定要舍入的模式和精度,这样就可以解决舍入的误差。
以使用 BigDecimal 类的 compareTo() 方法对两个数进行比较,该方法将会忽略小数点后的位数,怎么理解这句话呢?比如说 2.0 和 2.00 的位数不同,但它俩的值是相等的。
a.compareTo(b) 如果 a 和 b 相等,则返回 0,否则返回 -1。
tips: 不要使用
equals()方法对两个BigDecimal对象进行比较,这是因为equals()方法会考虑位数,如果位数不同,则会返回false,尽管数学值是相等的。
BigDecimal a = new BigDecimal("2.00");
BigDecimal b = new BigDecimal("2.0");System.out.println(a.equals(b));
System.out.println(a.compareTo(b) == 0);
上面的代码中 a.equals(b) 的结果就为 false,因为 2.00 和 2.0 小数点后的位数不同,但 a.compareTo(b) == 0 的结果就为 true,因为 2.00 和 2.0 在数学层面的值的确是相等的。
compareTo() 方法比较的过程非常严谨,源码如下:
private int compareMagnitude(BigDecimal val) {// Match scales, avoid unnecessary inflationlong ys = val.intCompact;long xs = this.intCompact;if (xs == 0)return (ys == 0) ? 0 : -1;if (ys == 0)return 1;long sdiff = (long) this.scale - val.scale;if (sdiff != 0) {// Avoid matching scales if the (adjusted) exponents differlong xae = (long) this.precision() - this.scale; // [-1]long yae = (long) val.precision() - val.scale; // [-1]if (xae < yae)return -1;if (xae > yae)return 1;if (sdiff < 0) {// The cases sdiff <= Integer.MIN_VALUE intentionally fall through.if (sdiff > Integer.MIN_VALUE &&(xs == INFLATED ||(xs = longMultiplyPowerTen(xs, (int) - sdiff)) == INFLATED) &&ys == INFLATED) {BigInteger rb = bigMultiplyPowerTen((int) - sdiff);return rb.compareMagnitude(val.intVal);}} else { // sdiff > 0// The cases sdiff > Integer.MAX_VALUE intentionally fall through.if (sdiff <= Integer.MAX_VALUE &&(ys == INFLATED ||(ys = longMultiplyPowerTen(ys, (int) sdiff)) == INFLATED) &&xs == INFLATED) {BigInteger rb = val.bigMultiplyPowerTen((int) sdiff);return this.intVal.compareMagnitude(rb);}}}if (xs != INFLATED)return (ys != INFLATED) ? longCompareMagnitude(xs, ys) : -1;else if (ys != INFLATED)return 1;elsereturn this.intVal.compareMagnitude(val.intVal);
}
接下来,用 BigDecimal 来解决开头的问题。
BigDecimal d1 = new BigDecimal("0.1");
BigDecimal three = new BigDecimal("3");
BigDecimal d2 = new BigDecimal("0.3");d1 = d1.multiply(three);System.out.println("d1 = " + d1);
System.out.println("d2 = " + d2);
System.out.println(d1.compareTo(d2));
程序输出的结果如下:
d1 = 0.3
d2 = 0.3
0
d1 和 d2 都为 0.3,所以 compareTo() 的结果就为 0,表示两个值是相等的。
总结一下,在遇到浮点数的时候,千万不要使用 == 操作符来进行比较,因为有精度问题。要么使用阈值来忽略舍入的问题,要么使用 BigDecimal 来替代 double 或者 float。
相关文章:
Java 如何正确比较两个浮点数
看下面这段代码,将 d1 和 d2 两个浮点数进行比较,输出的结果会是什么? double d1 .1 * 3; double d2 .3; System.out.println(d1 d2);按照正常逻辑来看,d1 经过计算之后的结果应该是 0.3,最后打印的结果应该是 tru…...
Qt 如何操作SQLite3数据库?数据库创建和表格的增删改查?
# 前言 项目源码下载 https://gitcode.com/m0_45463480/QSQLite3/tree/main # 第一步 项目配置 平台:windows10 Qt版本:Qt 5.14.2 在.pro添加 QT += sql 需要的头文件 #include <QSqlDatabase>#include <QSqlError>#include <QSqlQuery>#include &…...
【Hadoop】分布式文件系统 HDFS
目录 一、介绍二、HDFS设计原理2.1 HDFS 架构2.2 数据复制复制的实现原理 三、HDFS的特点四、图解HDFS存储原理1. 写过程2. 读过程3. HDFS故障类型和其检测方法故障类型和其检测方法读写故障的处理DataNode 故障处理副本布局策略 一、介绍 HDFS (Hadoop Distribute…...
【Python-随笔】使用Python实现屏幕截图
使用Python实现屏幕截图 环境配置 下载pyautogui包 pip install pyautogui -i https://pypi.tuna.tsinghua.edu.cn/simple/下载OpenCV包 pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple/下载PyQT5包 pip install PyQt5 -i https://pypi.tuna.tsi…...
Sun Apr 16 00:00:00 CST 2023格式转换
Date date new Date(); log.info("当前时间为:{}",date); //yyyy-MM-dd HH:mm:ss SimpleDateFormat sdf new SimpleDateFormat(DateUtils.YYYY_MM_DD_HH_MM_SS); String dateTime s…...
使用mongodb实现简单的读写操作
本文适合初学者,特别是刚刚安装了mongodb数据库的朋友,或在atlas刚拿到免费集群的朋友。 拿到数据库,心情很激动,手痒难耐。特别想向数据库插入几条数据库试试。即使是深夜完成了安装,也忍不住想去完成这些操作。看到…...
C语言实现Cohen_Sutherland算法
前提简要: 算法简介: 编码算法是最早、最流行的线段裁剪算法,该算法采用区域检验的方法,能够快速有效地判断一条线段与裁剪窗口的位置关系,对完全接受或完全舍弃的线段无需求交,即可直接识别。 算法思想&…...
MySQL进阶_EXPLAIN重点字段解析
文章目录 第一节.准备1.1 版本信息1.2 准备 第二节.type2.1 system2.2 const2.3 eq_ref2.4 ref2.5 ref_or_null2.6 index_merge2.7 unique_subquery2.8 range2.9 index2.10 all 第三节. Extra3.1 No tables used3.2 No tables used3.3 Using where3.4 No matching min/max row3…...
视图层与模板层
视图层 1 视图函数 一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是…...
MySQL数据库——触发器-案例(Insert类型、Update类型和Delete类型)
目录 表结构准备 插入数据触发器 代码 测试 修改数据触发器 代码 测试 删除数据触发器 代码 测试 通过触发器记录 tb_user 表的数据变更日志,将变更日志插入到日志表user_logs中,包含增加,修改,删除。 表结构准备 根据…...
快速创建桌面端(electron-egg)
介绍 | electron-egg electron-egg: 一个入门简单、跨平台、企业级桌面软件开发框架。 electron-egg是一个基于Electron和Egg.js的框架,可以用于快速构建跨平台的桌面应用程序。 1.兼容平台:electron-egg可以在Windows、MacOS和Linux等多个平台上运行…...
docker配置redis插件
docker配置redis插件 运行容器redis_6390 docker run -it \ --name redis_6390 \ --privileged \ -p 6390:6379 \ --network wn_docker_net \ --ip 172.18.12.19 \ --sysctl net.core.somaxconn1024 \ -e TIME_ZONE"Asia/Shanghai" -e TZ"Asia/Shanghai"…...
前端入口教程_web01
web标准 记得看! html:表示整个页面 head: titile: body: 常用标签 1.标题标签 2.段落标签 3.换行标签 4.文本格式化标签 5. 和 标签 6.图像标签 相对路径–用来插自己本地的图片 #### 绝对路径–用来插网上找的图…...
Win7 SP1 x64 Google Chrome 字体模糊
1 打开 Google Chrome ,地址栏输入 chrome://version/ ,字体模糊。 2 Microsoft Update Catalog 搜索更新 kb2670838,下载,安装,重启电脑。 3 打开 Google Chrome,地址栏输入 chrome://version/ ࿰…...
read()之后操作系统都干了什么
首先说明三个参数 file文件 buff从内存中开辟一段缓冲区用来接收读取的数据 size表示这个缓冲区的大小 有关file的参数: 状态:被打开 被关闭权限:可读可写最重要的是inode: 他包含了 文件的元数据(比如文件大小 文件类型 文件在访问前需要加…...
YoloV8改进策略:Swift Parameter-free Attention,无参注意力机制,超分模型的完美迁移
摘要 https://arxiv.org/pdf/2311.12770.pdf https://github.com/hongyuanyu/SPAN SPAN是一种超分网络模型。SPAN模型通过使用参数自由的注意力机制来提高SISR的性能。这种注意力机制能够增强重要信息并减少冗余,从而在图像超分辨率过程中提高图像质量。 具体来说,SPAN模…...
Python----练习:使用面向对象实现报名系统开发
第一步:分析哪些动作是由哪些实体发出的 学生提出报名 学生提供相关资料 学生缴费 机构收费 教师分配教室 班级增加学生信息 于是,在整个过程中,一共有四个实体:学生、机构、教师、班级!在现实中的一个具体的实…...
1.什么是html
1.什么是html什么是html? 一.基础信息 英文名字:HyperText Markup Language 中文名字:超文本标记语言 简称:html 文件格式:.htm 或 .html 结尾 作用:描述网页的语言。通过各种各样的标签,组…...
GeoServer漏洞(CVE-2023-25157)
前半部分是sql注入一些语句的测试,后面是漏洞的复现和利用 Sql注入漏洞 1.登入mysql。 2.查看默认数据库 3.使用mysql数据库 4.查看表 1.查看user 表 2.写注入语句 创建数据库 时间注入语句 布尔注入语句 报错注入语句 Geoserver漏洞ÿ…...
一个完整的手工构建的cuda动态链接库工程 03记
1, 源代码 仅仅是加入了模板函数和对应的 .cuh文件,当前的目录结构如下: icmm/gpu/add.cu #include <stdio.h> #include <cuda_runtime.h>#include "inc/add.cuh"// different name in this level for different type…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
