Java BigDecimal总结

news/2024/11/17 20:22:04/

文章目录

  • Java BigDecimal总结
    • 概述
    • float 和 double的问题
    • 创建BigDecimal对象
      • BigDecimal.valueOf()源码分析
    • equals() 和 compareTo()
    • BigDecimal设置精度和舍入模式
      • 舍入模式介绍
    • BigDecimal转字符串
    • DecimalFormat 格式化
    • 总结

Java BigDecimal总结

概述

BigDecimal类在Java的java.math包中,可以处理超过16位有效位数的精确运算,而双精度浮点类型double只能处理16位有效数。

float 和 double的问题

float a = 1.0F;
float b = 0.9F;
System.out.println(a - b); //0.100000024

问题分析:

执行结果是0.100000024而不是0.1,这是因为计算机的资源是有限的,所以没办法用二进制精确到表示0.1,只能用近似值来表示,也就是在有限的精度下,最大化接近0.1的二进制数,这样也就出现了精度缺少的问题。

创建BigDecimal对象

可以使用new BigDecimal() BigDecimal.value()的这2种方式创建BigDecimal对象。

BigDecimal a = new BigDecimal(0.1);
BigDecimal b = BigDecimal.valueOf(0.1);
System.out.println(a); //0.1000000000000000055511151231257827021181583404541015625
System.out.println(b); //0.1

问题分析:

使用new BigDecimal() 仍然会出现精度问题,而使用BigDecimal.valueOf()则不会出现精度问题。

BigDecimal.valueOf()源码分析

public static BigDecimal valueOf(double val) {return new BigDecimal(Double.toString(val));
}

valueOf()方法的内部是先调用toString()方法,将浮点类型转为字符串,然后再使用构造方法创建对象。因此在使用构造方法创建对象时,应优先传递字符串类型。

将参数改为字符串后没有了精度问题:

BigDecimal c = new BigDecimal("0.1");
System.out.println(c); //0.1

equals() 和 compareTo()

比较2个BigDecimal对象,可以使用equals()compareTo()

BigDecimal a = new BigDecimal("0.01");
BigDecimal b = new BigDecimal("0.010");
System.out.println(a.equals(b)); //false
System.out.println(a.compareTo(b)); //0

问题分析:

equals()方法不仅比较2个对象的值是否相等,还比较精度是否相同。

compareTo()方法仅比较值的大小,返回值为:

  • -1:小于
  • 0:等于
  • 1:大于

因此,如果只比较2个对象的值的大小,则使用compareTo();如果严格限制精度的比较,则使用equals()

BigDecimal设置精度和舍入模式

在使用BigDecimal进行运算时,一定要设置精度和舍入模式,否则会出现以下问题:

BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");
System.out.println(a.divide(b));

报异常:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.at java.base/java.math.BigDecimal.divide(BigDecimal.java:1722)

问题分析:

这是因为在做除法运算时,遇到一个无限小数0.3333…,不是一个精确到数字,因此抛出ArithmeticException异常。

舍入模式介绍

入模式定义在RoundingMode枚举类中,共有8种:

  • RoundingMode.UP:舍入远离零的舍入模式。在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。注意,此舍入模式始终不会减少计算值的大小。
  • RoundingMode.DOWN:接近零的舍入模式。在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。注意,此舍入模式始终不会增加计算值的大小。
  • RoundingMode.CEILING:接近正无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUNDUP 相同;如果为负,则舍入行为与 ROUNDDOWN 相同。注意,此舍入模式始终不会减少计算值。
  • RoundingMode.FLOOR:接近负无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUNDDOWN 相同;如果为负,则舍入行为与 ROUNDUP 相同。注意,此舍入模式始终不会增加计算值。
  • RoundingMode.HALF_UP:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。注意,这是我们在小学时学过的舍入模式(四舍五入)。
  • RoundingMode.HALF_DOWN:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。
  • RoundingMode.HALF_EVEN:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数,则舍入行为与 ROUNDHALFUP 相同;如果为偶数,则舍入行为与 ROUNDHALF_DOWN 相同。注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。如果前一位为奇数,则入位,否则舍去。以下例子为保留小数点1位,那么这种舍入方式下的结果。1.15 ==> 1.2 ,1.25 ==> 1.2
  • RoundingMode.UNNECESSARY:断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。

常用的四舍五入也就是RoundingMode.HALF_UP

加入精度和舍入模式后除法运算就正常了。

BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP)); //0.33

BigDecimal转字符串

BigDecimal对象转字符串有3种方法:

  • toPlainString():不适用科学计数法,通常使用最多。
  • toString():使用科学计数法。
  • toEngineeringString():使用工程计数法。

在这里插入图片描述

BigDecimal a = BigDecimal.valueOf(100000000000000000000.11);
System.out.println(a.toPlainString()); //100000000000000000000
System.out.println(a.toString()); //1.0E+20
System.out.println(a.toEngineeringString()); //100E+18

DecimalFormat 格式化

占位符说明
0数字占位符,位数不够时补0
#数字占位符,位数不够时省略
.小数点占位符
DecimalFormat format1 = new DecimalFormat("##.##");
System.out.println(format1.format(123.456)); //123.46
System.out.println(format1.format(3.4)); //3.4DecimalFormat format2 = new DecimalFormat("00.00");
System.out.println(format2.format(123.456)); //123.46
System.out.println(format2.format(3.4)); //03.40

总结

  1. 如果使用new BigDecimal()方式创建对象,一定要传入字符串;否则使用BigDecimal.valueOf()方式创建对象。
  2. BigDecimal对象比较值大小时推荐使用compareTo()方法。
  3. 使用BigDecimal运算时一定要设置精度和舍入模式。
  4. BigDecimal转字符串推荐使用toPlainString()方法。

http://www.ppmy.cn/news/7629.html

相关文章

补充订单不同的订单方式

其实补充订单不是就简简单单的下单评价,它里面有分很多种类的方法,不同时期店铺对补充订单的要求也不一样。 1.直播单 顾名思义,就是在挂着你产品链接里的直播间下单。 是需要要求买手跟主播互动咨询产品问题,发现其直播间&#x…

跟着pink老师学JS的第三天总结

* 这个仿京东的商品放大镜效果真不好做&#xff01; 鼠标拖拽&#xff1a; * 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"…

PLC实现十字路口交通灯的控制课程设计毕业设计

微信公众号&#xff1a;创享日记 对话框发送&#xff1a;plc十字路口 获取完整源码源程序文件 要求&#xff1a; 1、信号灯受启动及停止按钮的控制&#xff0c;当按下启动按钮时&#xff0c;信号灯系统开始工作&#xff0c;并周而复始地循环工作&#xff0c;当按下停止按钮时&…

用于高精度干涉仪的奇特量子效应

使用物质波放大、跟踪加速度&#xff0c;以前从未以便携式形式实现。&#xff08;图片来源&#xff1a;网络&#xff09; 来自法国的一组研究人员开发了第一个三向混合量子惯性传感器&#xff0c;它可以在不使用卫星信号的情况下测量加速度。这个突破性设备的核心是“物质波干涉…

ubuntu下编译opencv

目录 1. 下载opencv和opencv-contrib 2. 安装依赖 3. cmake 4. make 5. 安装 6. 配置opencv的路径 7. 测试 后续 1. 下载opencv和opencv-contrib https://github.com/opencv/opencv/archive/refs/tags/4.6.0.zip https://github.com/opencv/opencv_contrib/archive/re…

计算机内存机制精讲

全文目录1、一个程序在计算机中到底是如何运行的&#xff1f;2、虚拟内存到底是什么&#xff1f;虚拟地址中间层思想3、虚拟地址空间以及编译模式CPU的数据处理能力编译模式32位编译模式64位编译模式4、内存对齐&#xff0c;提高寻址效率5、内存分页机制&#xff0c;完成虚拟地…

华为OD机试真题 Python 实现【士兵过河】【2022.11 Q4 新题】

Python 题库目录 C++ 题库目录 Java 题库目录 目录 题目 思路 考点 Code 题目 一支N个士兵的军队正在趁夜色逃亡,途中遇到一条湍急的大河。 敌军在T的时长后到达河面,没到过对岸的士兵都会被消灭。 现在军队只找到

$nextTick 是干什么的

在vue中在父组件中可以操作子组件里的方法 在子组件上绑定 &#xff1a;ref " 自定义名 " 子组件绑定点击事件 click" doclick " 在meyhods 里定义执行函数 doClick&#xff08;&#xff09;{ this.自定义名.$refs.方法名 得到的就是子组件的dom&…