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
    }
}
相关推荐
一入程序无退路40 分钟前
若依框架导出显示中文,而不是数字
java·服务器·前端
little~钰40 分钟前
线段树和扫描线结合
数据结构·算法
tealcwu42 分钟前
【Unity基础】实现Scroll View跟随动态内容滚动
java·unity·游戏引擎
m0_6265352043 分钟前
代码分析 关于看图像是否包括损坏
java·前端·javascript
李贺梖梖44 分钟前
day06 二维数组、Arrays、System、HuTool、方法
java
pingzhuyan44 分钟前
linux常规(shell脚本)-启动java程序-实现快捷git拉取,maven打包,nohup发布(无dockerfile版)
java·linux·git·maven·shell
小股虫1 小时前
idea编译内存溢出 java: java.lang.OutOfMemoryError: WrappedJavaFileObject[ 解决方案
java·ide·intellij-idea·idea
CoderYanger1 小时前
动态规划算法-简单多状态dp问题:11.按摩师
开发语言·算法·leetcode·职场和发展·动态规划·1024程序员节
U***74691 小时前
三大框架-Spring
java·spring·rpc