Java中BigDecimal类与int、Integer使用总结
作者:BothEyes1993 发布时间:2023-08-09 15:36:44
前言
我们都知道浮点型变量在进行计算的时候会出现丢失精度的问题。如下一段代码:
System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.42);
System.out.println(4.015 * 100);
System.out.println(123.3 / 100);
输出:
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
可以看到在Java中进行浮点数运算的时候,会出现丢失精度的问题。那么我们如果在进行商品价格计算的时候,就会出现问题。很有可能造成我们手中有0.06元,却无法购买一个0.05元和一个0.01元的商品。因为如上所示,他们两个的总和为0.060000000000000005。这无疑是一个很严重的问题,尤其是当电商网站的并发量上去的时候,出现的问题将是巨大的。可能会导致无法下单,或者对账出现问题。所以接下来我们就可以使用Java中的BigDecimal类来解决这类问题。
Java中float的精度为6-7位有效数字。double的精度为15-16位
API
方法 描述
add(BigDecimal) BigDecimal对象中的值相加,然后返回这个对象。
subtract(BigDecimal) BigDecimal对象中的值相减,然后返回这个对象。
multiply(BigDecimal) BigDecimal对象中的值相乘,然后返回这个对象。
divide(BigDecimal) BigDecimal对象中的值相除,然后返回这个对象。
toString() 将BigDecimal对象的数值转换成字符串。
doubleValue() 将BigDecimal对象中的值以双精度数返回。
floatValue() 将BigDecimal对象中的值以单精度数返回。
longValue() 将BigDecimal对象中的值以长整数返回。
intValue() 将BigDecimal对象中的值以整数返回。
BigDecimal精度也丢失
我们在使用BigDecimal时,使用它的BigDecimal(String)构造器创建对象才有意义。其他的如BigDecimal b = new BigDecimal(1)这种,还是会发生精度丢失的问题。如下代码:
BigDecimal a = new BigDecimal(1.01);
BigDecimal b = new BigDecimal(1.02);
BigDecimal c = new BigDecimal("1.01");
BigDecimal d = new BigDecimal("1.02");
System.out.println(a.add(b));
System.out.println(c.add(d));
输出:
2.0300000000000000266453525910037569701671600341796875
2.03
可见论丢失精度BigDecimal显的更为过分。但是使用Bigdecimal的BigDecimal(String)构造器的变量在进行运算的时候却没有出现这种问题。 究其原因计算机组成原理里面都有,它们的编码决定了这样的结果。long可以准确存储19位数字,而double只能准备存储16位数字。double由于有exp位,可以存16位以上的数字,但是需要以低位的不精确作为代价。如果需要高于19位数字的精确存储,则必须用BigInteger来保存,当然会牺牲一些性能。所以我们一般使用BigDecimal来解决商业运算上丢失精度的问题的时候,声明BigDecimal对象的时候一定要使用它构造参数为String的类型的构造器。
同时这个原则Effective Java和MySQL
必知必会中也都有提及。float和double只能用来做科学计算和工程计算。商业运算中我们要使用BigDecimal。
正确运用BigDecimal
BigDecimal BigDecimal(double d); //不允许使用
BigDecimal BigDecimal(String s); //常用,推荐使用
static BigDecimal valueOf(double d); //常用,推荐使用
其原因有
double 参数的构造方法,不允许使用!!!!因为它不能精确的得到相应的值;
String 构造方法是完全可预知的: 写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好等于预期的0.1; 因此,通常建议优先使用 String 构造方法;
静态方法 valueOf(double val) 内部实现,仍是将 double 类型转为 String 类型; 这通常是将 double(或float)转化为 BigDecimal 的首选方法;
BigDecimal 的大小比较
例子:a.compareTo(b) < 0
compareTo 返回: -1,0,1
-1 小于
0 等于 1 大于
BigDecimal 的小数点后位数
BigDecimal c = new BigDecimal("2.224667").setScale(2, BigDecimal.ROUND_UP);
System.out.println(c);//2.23 跟上面相反,进位处理
----------
ROUND_CEILING 天花板(向上):正数进位向上,负数舍位向上
BigDecimal f = new BigDecimal("2.224667").setScale(2, BigDecimal.ROUND_CEILING);
System.out.println(f);//2.23 如果是正数,相当于BigDecimal.ROUND_UP
BigDecimal g = new BigDecimal("-2.225667").setScale(2, BigDecimal.ROUND_CEILING);
System.out.println(g);//-2.22 如果是负数,相当于BigDecimal.ROUND_DOWN
----------
ROUND_FLOOR 地板(向下):正数舍位向下,负数进位向下
BigDecimal h = new BigDecimal("2.225667").setScale(2, BigDecimal.ROUND_FLOOR);
System.out.println(h);//2.22 如果是正数,相当于BigDecimal.ROUND_DOWN
BigDecimal i = new BigDecimal("-2.224667").setScale(2, BigDecimal.ROUND_FLOOR);
----------
ROUND_HALF_UP
BigDecimal d = new BigDecimal("2.225").setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println("ROUND_HALF_UP"+d); //2.23 四舍五入(若舍弃部分>=.5,就进位)
----------
ROUND_HALF_DOWN
BigDecimal e = new BigDecimal("2.225").setScale(2, BigDecimal.ROUND_HALF_DOWN);
System.out.println("ROUND_HALF_DOWN"+e);//2.22 四舍五入(若舍弃部分>.5,就进位)
----------
int和Integer的区别
int是java提供的8种原始类型之一,java为每个原始类型提供了封装类,Integer是int的封装类。int默认值是0,而Integer默认值是null;
int和Integer(无论是否new)比较,都为true, 因为会把Integer自动拆箱为int再去比;
Integer是引用类型,用==比较两个对象,其实比较的是它们的内存地址,所以不同的Integer对象肯定是不同的;
但是对于Integer i=,java在编译时会将其解释成Integer i=Integer.valueOf();。但是,Integer类缓存了[-128,127]之间的整数, 所以对于Integer i1=127;与Integer i2=127; 来说,i1==i2,因为这二个对象指向同一个内存单元。 而Integer i1=128;与Integer i2=128; 来说,i1==i2为false。
各自的应用场景
Integer默认值是null,可以区分未赋值和值为0的情况。比如未参加考试的学生和考试成绩为0的学生
加减乘除和比较运算较多,用int
容器里推荐用Integer。 对于PO实体类,如果db里int型字段允许null,则属性应定义为Integer。 当然,如果系统限定db里int字段不允许null值,则也可考虑将属性定义为int。
对于应用程序里定义的枚举类型, 其值如果是整形,则最好定义为int,方便与相关的其他int值或Integer值的比较
Integer提供了一系列数据的成员和操作,如Integer.MAX_VALUE,Integer.valueOf(),Integer.compare(),compareTo(),不过一般用的比较少。建议,一般用int类型,这样一方面省去了拆装箱,另一方面也会规避数据比较时可能带来的bug。
来源:https://juejin.im/post/5d2195b0e51d4577407b1db2


猜你喜欢
- 用C#写的一个DVD管理器,供大家参考,具体内容如下(大神勿喷,初学者以借鉴为主)一共分为三个类分别是:DVD(启动类),XinXi(信息类
- 1.栈和队列的共同特点是(只允许在端点处插入和删除元素)4.栈通常采用的两种存储结构是(线性存储结构和链表存储结构)5.下列关于栈的叙述正确
- 这里我们给定一个集合strings一、写法1–循环for (int i = 0, len = strings.size(); i <
- 半路开始看的朋友可以回顾一下前几篇java并发编程专题(一)----线程基础知识java并发编程专题(二)----如何创建并运行java线程
- Toast是一种简易的消息提示框,它无法获取焦点,按设置的时间来显示完以后会自动消失,一般用于帮助或提示。先给大家分享下我的解决思路:不用计
- 本教程适合新手小白,Java7之前的版本是没有内置JavaFx的,Java7-10是内置JavaFx的,但是到了Java10以后的版本,Or
- 问题窥探在有些时候,我们拿到了一张图片。这张图片的格式是 JPG 还是 PNG?是 BMP 还是只有一帧的 GIF?虽然在大部分情况下,你确
- 由于接口使用的oracle字段长度为固定字节数,然后传进来的字符串估计比数据库字段的总字节数要大,那么截取小于数据库字节数的字符串。自己参考
- java后端介绍今天我正式开始了一个新话题,那就是 Web。目前我主要会介绍后端。作为后端的老大哥 java,也有很多后端框架,比如大家耳熟
- 前言dataGridView是常用的表格控件,实现分页的方式也有很多种,例如直接使用sql语言,配合存储方式,直接读取某一页的内容,大家如果
- Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, P
- 前几天有个客户在系统上写了一段html语句,打开页面就显示一张炒鸡大的图片,影响美观。后来仔细想想,幸亏注入的仅仅是html语句,知道严重性
- 简介本文介绍Idea如何根据maven依赖名查找它是哪个pom.xml引入的。有时候会有这样的问题:我们知道项目里用了某个依赖,想知道它是项
- ResultSet 动态获取列名和值仅供自己方便查阅,无其他用途ResultSet result = null; //前边SQL查询结果,这
- SSM@Controller,@Service本质都是@Component,作用是new对象放到Spring容器里。controller层@
- 最近由于工作要求:前端采用vue开发,后端采用springboot开发,前后端分离开发,最后前端页面又整合到后端来。经历多次采坑,总结以下方
- 泛型与类型擦除泛型,JDK 1.5新特性,本质是参数化类型(Parametersized Type) 的应用,即所操作的数据类型被指定为一个
- 一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如第6页用6表示而不是06或0
- 有时候需要动态添加数据,屏幕显示满了,数据需要滚动展示。这里主要弄懂scrollTo(0, off)方法的含义喊用法。含义不说了,大概意思就
- 0.写在前面2020-5-18更新这个东西已经是两年前的了,现在问我具体细节我也不是很清楚了,而且现在review两年前的代码感觉写的好烂。