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 标准,就会有精度的问题。
存储和转换的过程中浮点数容易引起一些较小的舍入误差,正是这个原因,导致在比较浮点数的时候,不能使用“==”操作符——要求严格意义上的完全相等。
那么如何正确的比较浮点数呢?这里有两种方案。
第一种方案是允许两个值之间存在一点误差(指定一个阈值),使用 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 相等。
第二种方案是使用 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 inflation
long 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 differ
long 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;
else
return 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如何正确比较浮点数的更多相关文章
- java中float/double浮点数的计算失精度问题(转)
如果我们编译运行下面这个程序会看到什么? public class Test { public static void main(String args[]) { ...
- 有趣的Java之调皮的浮点数
**当你在写一个电商网站的时候,你可能会给你的商品标价1.99,10.9这样的价格来吸引顾客.我应该用浮点数float/double来储存它们,当我的顾客购买商品的时候,从他们的账户里扣费,使用整型是 ...
- java 如何正确的输出集合或者对象的值
java 如何正确的输出集合或者对象的值 一般out.println(Object) 和 System.out.println(Object),其中输出的都是Object.toString()方法.重 ...
- Java比较两个浮点数
浮点数的基本数据类型不能用==比较,包装数据类型不能用 equals 比较 浮点数的表示 在计算机系统中,浮点数采用 符号+阶码+尾数 进行表示.在Java中,单精度浮点数float类型占32位,它的 ...
- 如何在 Java 中正确使用 wait, notify 和 notifyAll(转)
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- Java 中正确使用 hashCode 和 equals 方法
在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...
- 如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- Java中正确使用hashCode() 和equals() 、==
在java编程中,经常会遇到两个对象中某个属性的比较,常常会用到的方法有: == .equals().但是两者使用起来有什么区别呢? 一.== java中的==是比较两个对象在JVM中的地址.比较好理 ...
- java人员正确使用IDEA 的方式
博主是Java开发人员,以前一直都用myeclipse来开发的,说实话感觉myeclipse毫无美感可言,后来经过同事介绍,认识了IDEA,一眼就相中了IDEA黑色的主题风格,自此就抛弃了旧爱myec ...
随机推荐
- java多线程:线程池原理、阻塞队列
一.线程池定义和使用 jdk 1.5 之后就引入了线程池. 1.1 定义 从上面的空间切换看得出来,线程是稀缺资源,它的创建与销毁是一个相对偏重且耗资源的操作,而Java线程依赖于内核线程,创建线程需 ...
- TCHAR数据类型介绍
转载:https://blog.csdn.net/mousebaby808/article/details/5259944 并不是所有的Windows操作系统都支持UNICODE编码的API(例如早期 ...
- npm包管理器报错-npm ERR! Response timeout while trying to fetch https://registry.npmjs.org/@XXX(over 30000ms)
由于这两天买的新电脑在短期内频频蓝屏.卡机,不得不把自己其他的本本拿出来换上,但是程序员换电脑是真的痛苦,其他不说就说一个配环境 真的折腾哈 我是一名前端菜鸟,现在自己的本本上使用的是npm包管理工具 ...
- Github 太狠了,居然把 "master" 干掉了!
前段时间栈长有看到 Github 和 master 分支变更的新闻,当时没有注意细节,直到今天我创建仓库时: 看了半天感觉有点不对劲啊... 怎么 master 不见了,之前默认主干分支名称都是叫 m ...
- matplotlib画图教程,设置坐标轴标签和间距
大家好,欢迎来到周四数据处理专题,我们今天继续matplotlib作图教程. 在上周的文章当中我们介绍了如何通过xlabel和ylabel设置坐标轴的名称,以及这两个函数的花式设置方法,可以设置出各种 ...
- day37 Pyhton 网络编程04
# tcp协议和udp协议的选择问题 # tcp # 大量的连续的数据 传递文件\发送邮件 # 文件的传递 # 下载电影 # udp # 短消息类 社交软件 # qq 微信 # 在线播放视频 快会丢帧 ...
- Navicat Premium 15破解
https://pan.baidu.com/s/1bJ1BJUcL6cFd4StB5Qn_hQ 提取码:1314 拿走,不谢!!!
- main函数标准写法
main函数签名 C++中允许两种格式,带参数的和不带参数的: int main() int main(int argc, const char* argv[]) 和C标准不同,C++中main函数必 ...
- Linux ALSA音频库(二) 环境测试+音频合成+语音切换 项目代码分享
1. 环境测试 alsa_test.c #include <alsa/asoundlib.h> #include <stdio.h> // 官方测试代码, 运行后只要有一堆信息 ...
- java 调用WebService【转】
1.客户单编程方式(和第一种方式一样) //文件名:HelloService.java import javax.jws.WebMethod; import javax.jws.WebParam; i ...