Java BigDecimal与RoundingMode的用法总结

一、引进

做算法题的时候,最气人的不是不会写,而是会写却算错。明明逻辑满分,一输出"0.30000000000000004",瞬间被判死刑。别怀疑,就是 double 在背后捅刀------它眼里 0.1+0.2 永远不等于 0.3。

想甩掉这坑,只能把 double 踢下线,让 BigDecimal 上岗。它不讲二进制那一套,就按我们十进制算盘打,指哪打哪,小数点后面想留几位就留几位,永远对得上账。只要内存够,它能跟你耗到你先倒下,而且精度一分不差。

面对涉及金融计算、超大整数、或者需要绝对精确小数运算的题目时,

使用 BigDecimal

二、优点

1.算得准

double 眼里 0.1 其实是"0.100000000000000005...",攒多了就翻车。BigDecimal 把数字当"字符串"存,小学竖式怎么算它就怎么算,说 0.1 就 0.1,一分不差。

2. 数字再大也装得下

long 最大才 9 后面 18 个数字,题目动不动就要算 100 位的阶乘?BigDecimal 直接把数字当"无限长"的数组,只要内存不炸,它就能一直写下去。

3. 想怎么四舍五入你说了算

题目要"保留两位小数""银行家舍入""直接砍尾"?一行 setScale+RoundingMode,分分钟切好,double 却只能干瞪眼。

4. 绝不溢出

int、long 加着加着变负数?放心,BigDecimal 只要内存有多大,我就能算多大。

三、算法题中最常用的BigDecimal方法

构造方法:

BigDecimal(String val)最推荐、最安全的方式! 使用字符串构造可以精确表示你想要的数值

(如 new BigDecimal("0.1"))。避免使用BigDecimal(double val),因为传入的double本身可能已有精度损失(如 new BigDecimal(0.1) 实际上构造的是

0.1000000000000000055511151231257827021181583404541015625)。

BigDecimal.valueOf(double val): 这是将double转换为BigDecimal的相对安全方式。它内部调用Double.toString(double),利用了字符串转换的精确性。适用于已知double值能精确表示或精度要求高的场景(比直接使用double构造器好得多)。

算术运算:

add(BigDecimal augend): 加法
subtract(BigDecimal subtrahend): 减法
multiply(BigDecimal multiplicand): 乘法
divide(BigDecimal divisor): 除法

(注意: 如果结果无法精确表示(如1 / 3),会抛出ArithmeticException ,必须指定舍入模式!) divide(BigDecimal divisor, int scale, RoundingMode roundingMode): 最常用的除法形式!

指定结果的小数位数(scale)和舍入模式(roundingMode)。
divide(BigDecimal divisor, RoundingMode roundingMode): 除法,使用指定的舍入模式,结果精度由计算决定(通常位数较多)
abs(): 绝对值

pow(int n): 幂运算(n次方)

比较:

compareTo(BigDecimal val): 比较大小的标准方法

返回 -1(小于)、0(等于)、1(大于)。必须使用此方法代替equals()!

重要陷阱: equals()方法不仅比较数值是否相等 ,还比较精度(scale)是否相同

new BigDecimal("1.0").equals(new BigDecimal("1.00")) 会返回 false!

而compareTo() 只关心数值本身,1.0和1.00比较结果为0(相等)。

精度控制与舍入:

setScale(int newScale, RoundingMode roundingMode) : 核心精度控制方法! 设置小数点后的位数 (newScale),并使用指定的RoundingMode 进行必要的舍入操作。

如果当前scale大于newScale,需要舍入; 如果小于newScale,则在末尾补零。
scale(): 获取当前小数点后的位数。

输出:

toString():

返回此BigDecimal的规范字符串表示形式。如果需要特定格式(如固定小数位、科学计数法),应使用NumberFormat或DecimalFormat。
toPlainString():

返回不带指数字段的字符串表示。对于非常大或非常小的数,toString()可能输出科学计数法(如1e+10),而toPlainString()会输出"10000000000"。

四、RoundingMode:定义如何"舍"与"入"

RoundingMode是一个枚举类(java.math.RoundingMode),定义了8种舍入策略。它与BigDecimal的divide和setScale方法紧密结合,是精确控制结果的关键。算法竞赛中最常用的几种模式:

  1. RoundingMode.HALF_UP ("四舍五入")

    • 规则: 舍弃部分 >= 0.5 时进位;< 0.5 时舍弃。
    • 例子: 5.5 -> 6, 2.5 -> 3, 1.4 -> 1, 1.6 -> 2
    • 谐音:"哈夫上"
      画面:哈(HALF)佛老教授走上(UP)讲台------只上一半台阶就回头,"过半就上去"。
    • 竞赛应用: 题目明确要求"四舍五入"时使用。这是最符合直觉的舍入方式。
  2. RoundingMode.DOWN ("向零舍入")

    • 规则: 无论舍弃部分大小,总是向零的方向舍入(即绝对值变小)。
    • 例子: 5.9 -> 5, -5.9 -> -5, 1.1 -> 1, -1.1 -> -1
    • 谐音:"剁"
      画面:一把刀剁下去,向零剁,数字瞬间被砍到 0 那边。
    • 竞赛应用: 需要截断小数部分(取整)而不进行任何舍入时使用。类似于强制类型转换(int)的效果。
  3. RoundingMode.CEILING ("向正无穷舍入")

    • 规则: 结果总是向正无穷方向舍入(向上取整)。
    • 例子: 5.1 -> 6, -5.1 -> -5, 1.0 -> 1
    • 竞赛应用: 需要计算"至少需要多少个"的场景(如物品装箱、资源分配)。
  4. RoundingMode.FLOOR ("向负无穷舍入")

    • 规则: 结果总是向负无穷方向舍入(向下取整)。
    • 例子: 5.9 -> 5, -5.9 -> -6, 1.0 -> 1
    • 竞赛应用: 需要计算"最多能容纳多少个"的场景。
  5. RoundingMode.HALF_EVEN ("银行家舍入法")

    • 规则: 舍弃部分 > 0.5 时进位;< 0.5 时舍弃;等于 0.5 时,看前一位数字:如果是奇数则进位,如果是偶数则舍弃。
    • 例子: 5.5 -> 6 (5是奇数,进位), 2.5 -> 2 (2是偶数,舍弃), 1.6 -> 2, 1.4 -> 1
    • 竞赛应用: 在统计学或需要减少累计误差的场景中更优。题目没有特别说明"四舍五入"时,有时会默认使用此规则(需仔细审题!)

五、在算法题中使用BigDecimal的典型流程

  1. 构造

    优先使用String 构造或BigDecimal.valueOf()

  2. 运算

    使用add, subtract, multiply 进行基本运算。进行divide 时,一定要指定精度(scale)和舍入模式(RoundingMode)!

  3. 精度控制

    在最终输出结果前,使用setScale(int desiredScale, RoundingMode roundingMode)将 结果调整到题目要求的精度。

  4. 比较

    使用compareTo()比较两个BigDecimal的大小。

  5. 输出

    使用toString()或toPlainString()输出,或者根据题目要求使用格式化输出。

例题

计算圆的面积,保留两位小数,四舍五入

java 复制代码
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Scanner;

public class CircleArea {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        BigDecimal radius = new BigDecimal(sc.nextDouble());
        BigDecimal PI = new BigDecimal("3.14159");

        //计算面积
        BigDecimal area = PI.multiply(radius).multiply(radius);

        //设置精度,保留两位小数,四舍五入
        area = area.setScale(2,RoundingMode.HALF_UP);

        System.out.println(area);
        
        //输入:5.0
        //输出:78.54
    }
}
相关推荐
lihao lihao7 小时前
C++ set和map
开发语言·c++·算法
Mr -老鬼8 小时前
Java、Go、Rust高并发时代“称雄”之战:场景适配与生态博弈
java·golang·rust
学嵌入式的小杨同学8 小时前
顺序表(SqList)完整解析与实现(数据结构专栏版)
c++·算法·unity·游戏引擎·代理模式
vivo互联网技术8 小时前
vivo 微服务架构实践之 Dubbo 性能优化
java·后端·微服务·中间件·dubbo
格林威8 小时前
多光源条件下图像一致性校正:消除阴影与高光干扰的 6 个核心策略,附 OpenCV+Halcon 实战代码!
人工智能·数码相机·opencv·算法·计算机视觉·分类·视觉检测
仙俊红8 小时前
Spring 构造器注入 vs 字段注入
java·后端·spring
iAkuya8 小时前
(leetcode)力扣100 40二叉树的直径(迭代递归)
java·算法·leetcode
0和1的舞者8 小时前
Spring 事务核心知识点全梳理(编程式 + 声明式 + 注解详解)
java·后端·spring
橘颂TA8 小时前
【剑斩OFFER】算法的暴力美学——leetCode 103 题:二叉树的锯齿形层序遍历
算法·leetcode·结构与算法
2501_901147838 小时前
高性能计算笔记:灯泡开关问题的数学优化与常数级解法
笔记·算法·求职招聘