Java中浮点数的坑
基本数据类型






浮点数存在误差
浮点数有一个需要特别注意的点就是浮点数是有误差的,比如以下这段代码你觉得输出的什么结果:
public class Demo {
public static void main(String[] args) {
System.out.println(0.1+0.2 == 0.3);//输出false
}
}
这段代码输出值是false,之所以是这个结果那是因为浮点数是存在误差的,也就yi是说0.1在计算机中存储时不是精确的0.1,而有可能是0.1000000001,或者其他数,而0.2或0.3也是如此,所以0.1+0.2和0.3在计算机中是不相等的。
因为浮点数存在这个特性,所以我们在编程中间要尽量避免用浮点数进行比较。
如果非要用浮点数进行比较的话,那可以使用下面这个方法:
public class Demo {
public static void main(String[] args) {
float n = (float)1e-6;//表示10的-6次方
System.out.println(0.1+0.2 - 0.3 < n);//输出true
}
}
以上代码的输出值是true,该方法的原理是如果两个数相差足够小,小到可以忽略不记的话,这里的界限设置是10的-6次方,那证明比较的这两个数可以认为是相等的,此方法只能在所表示的浮点数的小数点后的位数不是很多的时候使用。
接下来我们再来看一种极端的情况,代码如下:
public class Demo {
public static void main(String[] args) {
System.out.println(0.30000000000000001 == 0.3);//输出true
}
}
以上的代码输出true,但其实我们肉眼可以很直观的看出,这两个数虽然很接近,但他们绝对不相等,像这种极端的数我们是无法用上面的方法进行比较的,所以还是记住这句话:尽量避免对浮点数进行比较。


BigDecimal类
我们既然知道了浮点数是存在误差的,所以在数据本身需要准确精度存储时,我们是一定不会使用float和double的,比如金钱数额的存储。这时我们通常使用BigDecimal类进行存储,它是一个可以存储准确浮点数的类。

BigDecimal类的定义:
BigDecimal bd = new BigDecimal("123.456");
BigDecimal使用scale()表示小数位数,例如:
BigDecimal d1 = new BigDecimal("987.65");
BigDecimal d2 = new BigDecimal("987.6500");
BigDecimal d3 = new BigDecimal("98765400");
System.out.println(d1.scale()); // 2,表示两位小数
System.out.println(d2.scale()); // 4
System.out.println(d3.scale()); // 0
BigDecimal中的stripTrailingZeros()方法,可以将BigDecimal格式化为去掉数值末尾0的相等的数:
BigDecimal d1 = new BigDecimal("123.4500");
BigDecimal d2 = d1.stripTrailingZeros();
System.out.println(d1+" "+d1.scale()); // 123.4500 4
System.out.println(d2+" "+d2.scale()); // 123.45 2,因为去掉了00
BigDecimal d3 = new BigDecimal("1234500");
BigDecimal d4 = d3.stripTrailingZeros();
System.out.println(d3+" "+d3.scale()); // 1234500 0
System.out.println(d4+" "+d4.scale()); // 1.2345E+6 -2
BigDecimal的scale()返回负数,例如,-2,表示这个数是个整数,并且末尾有2个0。以上的d4就是如此,去掉0后数值没变,只是换了一种表示方法。
BigDecimal可以设置它的scale,如果精度比原始值低,那么按照指定的方法进行四舍五入或者直接截断:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Demo {
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("123.456789");
BigDecimal d2 = d1.setScale(4, RoundingMode.HALF_UP); // 四舍五入,123.4568
BigDecimal d3 = d1.setScale(4, RoundingMode.DOWN); // 直接截断,123.4567
System.out.println(d2);//123.4568
System.out.println(d3);//123.4567
}
}
BigDecimal的加、减、乘、除:
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("124.44");
BigDecimal d2 = new BigDecimal("12.2");
System.out.println(d1.add(d2));//d1+d2 136.64
System.out.println(d1.subtract(d2));//d1-d2 112.24
System.out.println(d1.multiply(d2));//d1*d2 1518.168
System.out.println(d1.divide(d2));//d1/d2 10.2
}
}
BigDecimal在做加、减、乘时,精度不会丢失,但是做除法时,存在无法除尽的情况,这时就必须指定精度以及如何进行截断:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Demo {
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("23.456789");
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); // 保留10位小数并四舍五入
BigDecimal d4 = d1.divide(d2); // 报错:ArithmeticException,因为除不尽
}
}
- 可以对
BigDecimal做除法的同时求其余数:
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal n = new BigDecimal("22.444");
BigDecimal m = new BigDecimal("0.23");
BigDecimal[] dr = n.divideAndRemainder(m);
System.out.println(dr[0]); // 97.0
System.out.println(dr[1]); // 0.134
}
}
- 调用
divideAndRemainder()方法时,返回的数组包含两个BigDecimal,第一个是商,第二个是余数,商总是整数,余数不会大于余数,我们可以利用该方法判断两个BigDecimal是否是整数倍数:
BigDecimal n = new BigDecimal("12.34");
BigDecimal m = new BigDecimal("0.12");
BigDecimal[] dr = n.divideAndRemainder(m);
if (dr[1].signum() == 0) {//signum()会基于此BigDecimal返回三个值-1、1、0,分别对应为该数小于0,大于0和等于0
// n是m的整数倍
}
- 比较两个
BigDecimal的值是否相等时,要注意的是,使用equals()方法不但要求两个BigDecimal的值相等,还要求它们的scale()相等:
BigDecimal d1 = new BigDecimal("123.45");
BigDecimal d2 = new BigDecimal("123.45000");
System.out.println(d1.equals(d2)); // false,因为scale不同
System.out.println(d1.equals(d2.stripTrailingZeros())); // true,因为d2去除尾部0后scale变为2,与d1相同
注意:使用compareTo()来比较两个BigDecimal的值,不要用equals()
- 使用
compareTo()方法来比较两数大小,它根据两个值的大小分别返回-1、1和0,分别表示小于、大于和等于。
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("123.45");
BigDecimal d2 = new BigDecimal("123.45000");
BigDecimal d3 = new BigDecimal("123.40");
System.out.println(d1.compareTo(d2)); // 0
System.out.println(d1.compareTo(d3));// 1
System.out.println(d3.compareTo(d2));// -1
}
}
- 查看
BigDecimal的源码,可以发现一个BigDecimal是通过一个BigInteger和一个scale来表示的,即BigInteger表示一个完整的整数,而scale表示小数位数:
public class BigDecimal extends Number implements Comparable<BigDecimal> {
private final BigInteger intVal;
private final int scale;
}






更多精彩内容敬请关注微信公众号:【平兄聊Java】
Java中浮点数的坑的更多相关文章
- java中浮点数的比较(double, float)(转)
问题的提出:如果我们编译运行下面这个程序会看到什么? public static void main(String args[]){ System.out.println(0.05+0.01); Sy ...
- 计算价格, java中浮点数精度丢失的解决方案
计算价格, java中浮点数精度丢失的解决方案
- java中的那些坑
最近准备换工作,为了少让人家鄙视,就要狠狠地藐视这些面试题目.找了本电子书,发了有好多坑,都是特别简单,但是很少有人做对的题目.面对这样的题目,我却有一种兴奋的感觉,也许是因为一直做着重复的工作没有新 ...
- Java 中浮点数---------BigDecimal和double(初探)
为什么要使用 bigdecimal? 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了 ...
- Java中浮点数能连续精确表示整数的范围
转自http://blog.csdn.net/seizef/article/details/5571783#ref_1,有删改. 先简单介绍一下浮点数在计算机中的组成,在Java中采用的浮点数表示法是 ...
- Java中浮点数的精度问题 【转】
当您在计算Money的时候,请看好了!!!要不损失了别后悔!!! 现象1: public static void main(String[] args) { System.out.println(0. ...
- java 中==符号的坑
在某技术群看到这样的一个面试题目: 这是一个4年经验的java 从业者的答案. 你的答案是什么呢? 正确的答案是true. 为什么? 其实当使用String a="a"+" ...
- Java中浮点数的基础知识
偶然查看Math.round的JDK public static int round(float a) { if (a != 0x1.fffffep-2f) // greatest float val ...
- java第二周的学习知识4(对原码,补码,反码和java中浮点数计算不准确的总结)
原码:一个正数,转换为二进制位就是这个正数的原码.负数的绝对值转换成二进制位然后在高位补1就是这个负数的原码. 但是原码有几个缺点,零分两种 +0 和 -0 .很奇怪是吧!还有,在进行不同符号的加法运 ...
随机推荐
- 扩展欧几里得算法(EXGCD)学习笔记
0.前言 相信大家对于欧几里得算法都已经很熟悉了.再学习数论的过程中,我们会用到扩展欧几里得算法(exgcd),大家一定也了解过.这是本蒟蒻在学习扩展欧几里得算法过程中的思考与探索过程. 1.Bézo ...
- 【牛客网】数据库SQL实战(题解)
1.查找最晚入职员工的所有信息 [题解] hire_date可能存在重复值,所以需要找到hire_date的最大值,然后再筛选,才能hire_date最晚的记录都筛选出来. [代码] 1 SELECT ...
- 5行代码!完成bat病毒制作!!!
这个病毒的功能是删除当前目录下.cpp类型的代码. copy %0 "%userprofile%\AppData\Roaming\Microsoft\Windows\Start Menu\P ...
- 《逆向工程核心原理》——DLL注入与卸载
利用CreateRemoteThread #include <iostream> #include <tchar.h> #include <Windows.h> # ...
- 2019_西湖论剑_预选赛 testre
2019_西湖论剑_预选赛 testre 程序中关键操作是比较ptr,其中夹杂的一部分v26计算是为了混淆我们的分析.那么我们只要跟踪ptr数组的生成便可,向上发现v11,加密操作数组. 接下来跟踪v ...
- 详解 ZooKeeper 数据持久化
本文作者:HelloGitHub-老荀 Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费开源.有趣.入门级的 ZooKeeper 教程,面向有编程基础的新手. 项 ...
- Java系列教程-SpringMVC教程
SpringMVC教程 1.SpringMVC概述 1.回顾MVC 1.什么是MVC MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范. 是将业务 ...
- C语言之漫谈指针(下)
C语言之漫谈指针(下) 在上节我们讲到了一些关于指针的基础知识: 详见:C语言之漫谈指针(上) 本节大纲: 零.小tips 一.字符指针 二.指针数组与数组指针 三.数组传参与指针传参 四.函数指针及 ...
- effective解读-第八条 避免使用finalizer和Cleaner
java9之前finalizer,java9使用cleaner代替了finalizer.相比finalizer,cleaner(它存在于一个独立类Cleaner中,需要时候注入到对应类中即可)不会污染 ...
- Java例题_30 在已经排好序的数组中插入值
1 /*30 [程序 30 插入数字] 2 题目:有一个已经排好序的数组.现输入一个数,要求按原来的规律将它插入数组中. 3 程序分析:首先判断此数是否大于最后一个数,然后再考虑插入中间的数的情况,插 ...