点击上方“方志朋”,选择“设为星标”

做积极的人,而不是积极废人

本文来源于读者投稿,已授权原创申明。

前言:BigDecimal的产生背景

在Java的8种基本类型中,我们知道double和float基本数据类型存在着精度缺失问题。我们先来看一个例子。

        double augend = 1.0000001;double addend = 0.0000001;double sum = augend + addend;//sum: 1.0000002000000001System.out.println("sum: " + sum);

可以看到sum并不是我们所想要的1.0000002,而是1.0000002000000001。虽然误差非常小,但是如果开发银行金融类产品时,我们就需要绝对精确的数据。试想下这样一个场景,在一个用户余额为99.95元时,恰好他要购买总价99.95元的商品,而程序中定义总价时使用了double类型,这就有可能使得总价大于余额,导致用户无法购买商品。不过,Java提供了BigDecimal类用来了解决此类问题。

一. 如何使用BigDecimal

本文将从一个实际生活场景出发——超市购物,通过在买单时计算总消费金额来介绍BigDecimal的基本使用方法(加减乘除等)。由这个生活中的具体例子出发,加深我们对BigDecimal的记忆和理解。

1.1 初始化BigDecimal对象

BigDecimal的源码中有16个构造函数和3个静态方法可以用于初始化BigDecimal对象。如图所示:本文会重点介绍上图所标记的三种方法。我们先建立一个Receipt类,在该类中定义四个常量并使用main方法将它们打印出来。从控制台的输出结果中,我们可以看到使用参数类型为String类型的构造函数创建的MEATPRICE对象打印结果为19.98,而使用参数类型为double类型的构造函数创建的RICEPRICE对象打印结果为3.3300000000000000710542735760100185871124267578125(每个机器可能不一样)。当我们的数量足够多的时候,就会产生导致总价产生较大的误差,这是我们不希望看到的。同时,在使用静态方法valueOf的时候,也能得到精确的数字。这是因为源码中调用了Double类型的toString方法,然后再调用了参数类型为String类型的构造函数创建该对象。

1.2 BigDecimal的加减乘除等实例方法

在进行下面的操作之前,我们将RICE_PRICE改为使用valueOf方法初始化。我们在main方法中定义了double类型的weightOfMeat、weightOfRice变量和int类型的quantityOfVinda变量。同时,我们定义了一张面值100购物卡,为了方便演示,只是定义BigDecimal的amount对象,并且利用BigDecimal的常量ZERO创建totalPrice对象。在使用BigDecimal的实例方法时,如multiply(乘法)方法,需要传入BigDecimal类型参数。同样,在加减和除的方法中,也是需要传入BigDecimal类型参数。所以在进行计算前,我们调用了valueOf类方法,用以获取对应数值的BigDecimal对象。从上图中,我们可以看出“猪肉总价为:44.9550”。虽然计算结果正确,但是却不符合我们实际生活需求。因为在超市的小票上,数字只有两位小数,所以我们在计算riceTotalPrice时,调用了实例方法setScale,该方法一共有三个重载方法,最常用的就是我们所使用的这种。在设置了小数点位数和舍入模式后,我们可以看到“大米总价为:84.42”,84.42才符合我们的实际需求。接下来,我们将通过BigDecimal的add(加法)计算出totalPrice,再通过BigDecimal的subtract(减法)计算amount减去totalPrice,获得BigDecimal的differencePrice(差价)对象。通过BigDecimal的compareTo方法比较difference与BigDecimal.ZERO的大小关系,得出客户使用了购物卡后是否还需再付钱,如还要付钱,将调用BigDecimal的abs方法将difference取绝对值。在这一次的购物的过程中,我们学会了BigDecimal的初始化方法,加减法,乘法和compareTo方法。不过,在比较两个值是否相等还有equals方法,这个方法要谨慎使用。因为这个equals方法不仅要比较数值是否相同,还要比较精度是否相同。也就是说,如果2.0和2.00用equals方法进行比较,返回的结果为false。到目前为止,还有一个常用的方法——divide(除法),我们没学到。现在,我们就来使用divide算一算是买清风卷纸划算还是买维达卷纸划算。通过使用divide方法,我们计算出了qingFengSingle和vindaSingle。在调用divide的过程中,我们指定了保留的小数点位数和舍入模式。在BIgDecimal中divide一共有6个重载方法,其形参不同之处在于是否有精度参数,是否有舍入模式参数及舍入模式参数的类型。

1.3 八种舍入模式

在上面的代码中,我们在为setScale或divide指定舍入模式参数时,是通过RoundingMode的枚举指定的。RoundingMode的枚举对象一共有8个,分别为:RoundingMode.UP、RoundingMode.DOWN、RoundingMode.CEILING、RoundingMode.FLOOR、RoundingMode.HALFUP、RoundingMode.HALFDOWN、RoundingMode.HALF_EVEN和RoundingMode.UNNECESSARY。RoundingMode枚举类有一个类型为int的oldModel变量,从0-7依次对应上面8种枚举对象。关于枚举类型的简单介绍,可参考博主的《JAVA枚举类型(Enum)的使用》

  • RoundingMode.UP:舍入远离零的舍入模式。在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。注意,此舍入模式始终不会减少计算值的大小。

  • RoundingMode.DOWN:接近零的舍入模式。在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。注意,此舍入模式始终不会增加计算值的大小。

  • RoundingMode.CEILING:接近正无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUNDUP 相同;如果为负,则舍入行为与 ROUNDDOWN 相同。注意,此舍入模式始终不会减少计算值。

  • RoundingMode.FLOOR:接近负无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUNDDOWN 相同;如果为负,则舍入行为与 ROUNDUP 相同。注意,此舍入模式始终不会增加计算值。

  • RoundingMode.HALFUP:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。如果舍弃部分 >= 0.5,则舍入行为与 ROUNDUP 相同;否则舍入行为与 ROUND_DOWN 相同。注意,这是我们在小学时学过的舍入模式(四舍五入)。

  • RoundingMode.HALFDOWN:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。如果舍弃部分 > 0.5,则舍入行为与 ROUNDUP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。

  • RoundingMode.HALFEVEN:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数,则舍入行为与 ROUNDHALFUP 相同;如果为偶数,则舍入行为与 ROUNDHALF_DOWN 相同。注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。如果前一位为奇数,则入位,否则舍去。以下例子为保留小数点1位,那么这种舍入方式下的结果。1.15 ==> 1.2 ,1.25 ==> 1.2

  • RoundingMode.UNNECESSARY:断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。

二. 总结

对于BigDecimal类,我们已经掌握了基本的用法,这些用法也是开发过程中最常见的用法。BigDecimal是可以绝对控制小数点位数的一种数据类型,它适用于银行金融,商城类业务开发。同时,作为一种数据类型,其最根本的功能就是对数据进行运算,这也是本文的核心内容。参考网址:BigDecimal加减乘除计算;廖雪峰的官方网站。代码:

public class Receipt {//猪肉单价(单位:kg)private static final BigDecimal MEAT_PRICE = new BigDecimal("19.98");//大米单价(单位:kg)private static final BigDecimal RICE_PRICE = new BigDecimal(3.33D);//清风卷纸(12包装)单价private static final BigDecimal QINGFENG_ROLL_PAPER_PRICE = BigDecimal.valueOf(25D);//维达卷纸(20包装)单价private static final BigDecimal VINDA_ROLL_PAPER_PRICE = BigDecimal.valueOf(50D);public static void main(String[] args) {//购物卡余额:100BigDecimal amount = BigDecimal.valueOf(100D);//总价初始化为0BigDecimal totalPrice = BigDecimal.ZERO;//猪肉净含量double weightOfMeat = 2.25D;//大米净含量double weightOfRice = 25.35D;//卷纸数量int quantityOfVinda = 1;//猪肉总价:44.9550BigDecimal meatTotalPrice = MEAT_PRICE.multiply(BigDecimal.valueOf(weightOfMeat)).setScale(2, RoundingMode.HALF_UP);//大米总价:84.42// 3.33 * 25.35 = 84.4155,通过setScale设置保留的2位小数,并且设置舍入模式为四舍五入BigDecimal riceTotalPrice = RICE_PRICE.multiply(BigDecimal.valueOf(weightOfRice)).setScale(2, RoundingMode.HALF_UP);//维达卷纸总价:50.00BigDecimal vindaRollPaperPrice = VINDA_ROLL_PAPER_PRICE.multiply(BigDecimal.valueOf(quantityOfVinda)).setScale(2, RoundingMode.HALF_UP);//通过add(加法)计算总价totalPrice:179.38totalPrice = totalPrice.add(meatTotalPrice).add(riceTotalPrice).add(vindaRollPaperPrice);//通过subtract(减法)计算差价differencePrice:-79.38BigDecimal differencePrice = amount.subtract(totalPrice);if (differencePrice.compareTo(BigDecimal.ZERO) < 0) {System.out.println("请付款:" + differencePrice.abs() + "元," + "购物卡余额:0元.");}else{System.out.println("购物卡余额:" + differencePrice + "元.");}//通过divide(除法)计算一包清风纸的价格,设置采用四舍五入模式保留2位小数,并使用doubleValue方法将结果转化成double类型double qingFengSingle = QINGFENG_ROLL_PAPER_PRICE.divide(BigDecimal.valueOf(12),2, RoundingMode.HALF_UP).doubleValue();//通过divide(除法)计算一包维达纸的价格,double vindaSingle = VINDA_ROLL_PAPER_PRICE.divide(BigDecimal.valueOf(20),2, RoundingMode.HALF_UP).doubleValue();int result = Double.compare(qingFengSingle, vindaSingle);if (result < 0) {System.out.println("一包清风纸价钱" + qingFengSingle + "元,小于一包维达纸价钱" + vindaSingle + "元。所以购买清风更划算!");}else if (result == 0){System.out.println("一包清风纸价钱" + qingFengSingle + "元,等于一包维达纸价钱" + vindaSingle );}else {System.out.println("一包清风纸价钱" + qingFengSingle + "元,大于一包维达纸价钱" + vindaSingle + "元。所以购买维达更划算!");}}
}

热门内容:   

  • Java分布式 RPC 框架性能大比拼,Dubbo最差?

  • 面试官:MySQL 表设计要注意什么?

  • 面试官问:平时碰到系统CPU飙高和频繁GC,你会怎么排查?

  • Java 线程池 ThreadPoolExecutor 八种拒绝策略浅析

  • Spring Boot 实现定时任务的 4 种方式

  • Java 程序员常用资源工具集合(建议收藏)

  • 你可能不需要微服务

  • 我在 GitHub 上都见过哪些沙雕项目?

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

如何使用BigDecimal?相关推荐

  1. BigDecimal 转换为int 或者其他类型

    我列举了转换的方法 BigDecimal HALF_UP = new BigDecimal("-12.44").setScale(1, RoundingMode.HALF_UP); ...

  2. Android 金钱计算BigDecimal 的使用

    今天做了一个购物车的功能 ,设计到了钱的计算,使用到了BigDecimal 晚上回来总结下 这个BigDecimal 计算钱呢很好用 其主要有 1 add 加法 2 subtract 减法 3 mul ...

  3. 在进行商业运算时解决BigDecimal的精度丢失问题

    System.out.println(0.05+0.01); System.out.println(1.0-0.42); System.out.println(4.015*100); System.o ...

  4. java striptrailingzeros_java – 为什么不BigDecimal.stripTrailingZeros()总是删除所有尾随零?...

    我做了以下事情 MathContext context = new MathContext(7, RoundingMode.HALF_UP); BigDecimal roundedValue = ne ...

  5. js 实现精确加减乘除运算之BigDecimal.js

    在前端实际开发中,进行前端计算会出现丢失精度的问题,这里我们项目中运用了BigDecimal.js. js计算丢失精度原因 计算机的二进制实现和位数限制有些数无法有限表示.就像一些无理数不能有限表示, ...

  6. Java中BigDecimal工具类(支持空值运算版)

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 前言 需要计算的场景绕不过去会使用BigDecimal类, ...

  7. 为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! BigDecimal,相信对于很多人来说都不陌生,很多人都 ...

  8. 面试:BigDecimal一定不会丢失精度吗?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源 | urlify.cn/ZVN7Nb 我们基本已经形成了常识 ...

  9. Java中的BigDecimal类你真的了解吗?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:HikariCP www.jianshu.com/p/c81 ...

最新文章

  1. 导航条——收缩式导航菜单
  2. GoldenGate字段和数据筛选
  3. 一种医学图像分割的新思路【nnU-Net网络配置教程】
  4. python代码翻译-python实现在线翻译
  5. 转载:【OpenCV入门教程之四】 ROI区域图像叠加初级图像混合 全剖析
  6. navicat开启mysql数据库root用户的远程访问
  7. Windows11 发布更新 Insider Preview Build 22000.100
  8. Educational Codeforces Round 48
  9. 18张图,详解SpringBoot解析yml全流程
  10. Lock锁的简单使用
  11. P1043 数字游戏
  12. 373.查找和最小的K对数字
  13. 大学生html电影网页设计作业成品 我喜欢的电影榜单题材网页制作代码 简单静态网页模板下载
  14. 岛屿周长c语言,[IOI2008]Island 岛屿
  15. php后台如何添加sitemap,织梦后台的sitemap生成及推送教程
  16. 学习Android启动初始化 App StartUp
  17. 微动开关技术知识-微动开关概要
  18. 分布式技术与实战第六课 分布式缓存设计
  19. Git与Bitbucket配合使用教程
  20. C语言_学习笔记_(一)程序设计与C语言

热门文章

  1. PHP实现队列的原理
  2. Android深度探索--HAL与驱动开发----第一章读书笔记
  3. static用法总结
  4. myeclipse中安装svn插件
  5. 【青少年编程】【三级】躲避恐龙
  6. 【建模必备】遗传算法的基本原理与步骤(选择)
  7. 【MATLAB】稀疏矩阵(含有大量0元素的矩阵)
  8. 低代码、RPA 和 AI,有什么区别
  9. AI化身监工,上班还能摸鱼吗?
  10. 中国人工智能市场破 50 亿!你还不了解 AI 云服务吗?