浅谈java中BigDecimal的equals与compareTo的区别

本文通过实例对比了BigDecimal中equals与compareTo方法的区别。equals方法不仅比较数值大小还比较精度,而compareTo仅比较数值。测试代码展示了不同情况下两者的行为差异。

这两天在处理支付金额校验的时候出现了点问题,有个金额比较我用了BigDecimal的equals方法来比较两个金额是否相等,结果导致金额比较出现错误(比如3.0与3.00的比较等)。

【注:以下所讲都是以sun jdk 1.4.2版本为例,其他版本实现未必一致,请忽略】

首先看一下BigDecimal的equals方法:

?
1
2
3
4
5
6
7
public boolean equals(Object x){
     if (!(x instanceof BigDecimal))
       return false ;
     BigDecimal xDec = (BigDecimal) x;
 
     return scale == xDec.scale && intVal.equals(xDec.intVal);
   }

可以看到BigDecimal的euquals方法是先判断要比较的数据类型,如果对象类型一致前提下同时判断精确度(scale)和值(BigInteger的equals方法)是否一致。

其实javadoc里面就已经写的很明白:“Compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).”只是自己没有去注意罢了!

再看一下compareTo方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
public int compareTo(BigDecimal val){
     /* Optimization: would run fine without the next three lines */
     int sigDiff = signum() - val.signum();
     if (sigDiff != 0)
       return (sigDiff > 0 ? 1 : -1);
 
     /* If signs match, scale and compare intVals */
     BigDecimal arg[] = new BigDecimal[ 2 ];
     arg[ 0 ] = this ;  arg[ 1 ] = val;
     matchScale(arg);
     return arg[ 0 ].intVal.compareTo(arg[ 1 ].intVal);
   }

可以看到这个方法里面有个matchScale的处理,意思是把精确度低的那个对象转换为高精确度,然后再进行比较(同样是BigInteger的compareTo方法),matchScale的实现如下:

?
1
2
3
4
5
6
private static void matchScale(BigDecimal[] val) {
     if (val[ 0 ].scale < val[ 1 ].scale)
       val[ 0 ] = val[ 0 ].setScale(val[ 1 ].scale);
     else if (val[ 1 ].scale < val[ 0 ].scale)
       val[ 1 ] = val[ 1 ].setScale(val[ 0 ].scale);
   }

做个简单测试:

?
1
2
3
System.out.println( new BigDecimal( "1.2" ).equals( new BigDecimal( "1.20" ))); //输出false
 
System.out.println( new BigDecimal( "1.2" ).compareTo( new BigDecimal( "1.20" )) == 0 ); //输出true

另外注意到我上面BigDecimal的构造方法里面传入的都是字符串,如果传入的是数字类型的话会有什么结果,大家可以自己测试一下,然后分析一下原因:

?
1
2
3
4
5
6
7
8
System.out.println( new BigDecimal( "1.2" ).equals( new BigDecimal( "1.20" ))); //输出false
System.out.println( new BigDecimal( "1.2" ).compareTo( new BigDecimal( "1.20" )) == 0 ); //输出true
     
System.out.println( new BigDecimal( 1.2 ).equals( new BigDecimal( "1.20" ))); //输出是?
System.out.println( new BigDecimal( 1.2 ).compareTo( new BigDecimal( "1.20" )) == 0 ); //输出是?
   
System.out.println( new BigDecimal( 1.2 ).equals( new BigDecimal( 1.20 ))); //输出是?
System.out.println( new BigDecimal( 1.2 ).compareTo( new BigDecimal( 1.20 )) == 0 ); //输出是?

最后结论是:对于BigDecimal的大小比较,用equals方法的话会不仅会比较值的大小,还会比较两个对象的精确度,而compareTo方法则不会比较精确度,只比较数值的大小。


转载自:https://www.jb51.net/article/96859.htm


<think>好的,我现在要详细分析用户关于JavaBigDecimalcompareTo()方法的问题。用户明确询问了该方法的用法、工作原理以及示例。我需要结合用户提供的引用内容以及之前的回答来组织信息。 首先,用户之前的问题是关于如何判断BigDecimal是否相等,而我在之前的回答中提到了compareTo()作为推荐方法,因为它忽略标度,仅比较数值。现在用户深入询问这个方法的具体使用和工作原理,所以需要详细展开。 根据引用内容,特别是引用[3]提到compareTo()会忽略精度,只比较数值是否相等。引用[4]和[5]提供了示例代码,说明当数值相同但标度不同时,compareTo()返回0,视为相等。同时引用[5]指出,即使标度不同,只要数值相同,就被视为相等。这验证了compareTo()的核心工作原理。 接下来,我需要解释方法的语法、返回值以及使用场景。用户可能需要知道如何正确调用这个方法,比如参数是什么,返回值的含义。根据示例,比如引用[3]中的代码,当value1是3.000000,value2是3时,compareTo()返回0,说明相等。这需要明确说明。 另外,用户可能想知道compareTo()equals()的区别,这在之前的回答中提到过,这里需要再次强调,因为equals()会检查标度和数值,而compareTo()仅数值。需要举例说明,比如不同标度的对象使用compareTo()返回0,但equals()返回false。 然后,关于工作原理,需要解释BigDecimal的内部比较机制,如何忽略标度进行数值比较。可能需要提到BigDecimal比较时会将两个数调整为相同的标度,然后比较它们的非标度值。例如,10.00和10.0的非标度值都是10,但标度不同,compareTo()仍视其为相等。 还需要注意用户提供的引用中的错误,比如引用[1]中提到compareTo()方法有三种,但实际并非如此。正确的应该是compareTo()、equals()和可能的其他方法。这里需要纠正,避免误导用户。 此外,用户可能需要更多的示例来理解不同情况下的返回值,比如当数值不同时,返回1或-1的情况。引用[4]中的示例展示了当两个数值不同时,compareTo()返回1的情况,需要详细说明这些情况。 最后,确保回答结构清晰,分点解释,提供代码示例,并总结最佳实践,帮助用户正确使用compareTo()方法。同时,需要生成相关问题,引导用户进一步学习,比如关于精度处理或其他比较方法区别。</think>### JavaBigDecimal的`compareTo()`方法详解 `BigDecimal`的`compareTo()`方法是数值比较的核心工具,其工作原理和正确使用方式直接影响精确计算的准确性。以下是**分步解析+底层逻辑+示例演示**: --- ### 一、方法定义语法 ```java public int compareTo(BigDecimal other) ``` - **参数**:待比较的另一个`BigDecimal`对象 - **返回值**: - `0`:当前对象参数**数值相等**(忽略标度差异) - `1`:当前对象**大于**参数 - `-1`:当前对象**小于**参数[^3][^5] --- ### 二、核心工作原理 1. **数值标准化处理**: - 将两个`BigDecimal`对象调整到**相同的标度**(通过数学等价转换) - 例如:$5.0 \rightarrow 50 \times 10^{-1}$,$5.00 \rightarrow 500 \times 10^{-2}$ 2. **非标度值比较**: - 直接比较转换后的整数部分(即去掉小数点后的原始数值)[^5] - 示例: ```java new BigDecimal("10.0").compareTo(new BigDecimal("10")) // 10*10^1 vs 10*10^0 → 100 vs 10 → 返回1(错误示例) ``` **修正说明**:实际比较时系统会通过数学等价调整到同一标度,因此上述示例应返回0[^3] --- ### 三、使用场景示例 #### 场景1:基本数值比较 ```java BigDecimal a = new BigDecimal("200.00"); BigDecimal b = new BigDecimal("200"); System.out.println(a.compareTo(b)); // 输出0(数值相等) ``` #### 场景2:大小关系判断 ```java BigDecimal price1 = new BigDecimal("99.99"); BigDecimal price2 = new BigDecimal("100.00"); int result = price1.compareTo(price2); if(result < 0) { System.out.println("price1更便宜"); // 实际输出 } ``` #### 场景3:带科学计数法的比较 ```java BigDecimal sciNum = new BigDecimal("2.5E+3"); // 2500 BigDecimal normalNum = new BigDecimal("2500"); System.out.println(sciNum.compareTo(normalNum)); // 输出0 ``` --- ### 四、`equals()`的关键区别 | 对比维度 | `compareTo()` | `equals()` | |----------------|-----------------------------|-----------------------------| | 比较基准 | 仅数值内容 | 数值+标度(小数点位数) | | 返回true条件 | $3.0 = 3.00$ | $3.0 \neq 3.00$ | | 内存消耗 | 无需创建新对象 | 常需配合`setScale()`使用 | | 使用建议 | **推荐用于常规数值比较** | 仅需精确匹配格式时使用 | **示例验证**: ```java BigDecimal x = new BigDecimal("7.50"); BigDecimal y = new BigDecimal("7.5"); System.out.println(x.compareTo(y)); // 0(数值相同) System.out.println(x.equals(y)); // false(标度不同) ``` --- ### 五、底层实现解析(JDK源码逻辑) 查看`BigDecimal`源码可见关键比较逻辑: ```java // 简化后的核心代码段 private int compareMagnitude(BigDecimal other) { // 调整标度至相同值 long adjustedScale = (long)this.scale - other.scale; BigInteger thisInt = this.inflated().multiply(tenToThe(adjustedScale)); BigInteger otherInt = other.inflated(); // 直接比较调整后的整数 return thisInt.compareTo(otherInt); } ``` - **标度对齐**:通过乘以10的幂次统一标度(如$1.5 \rightarrow 15 \times 10^{-1}$,$1.50 \rightarrow 150 \times 10^{-2}$) - **整数比较**:将问题转化为`BigInteger`的数值比较[^5] --- ### 六、特殊场景处理 #### 1. 不同精度构造器的比较 ```java BigDecimal d1 = new BigDecimal(2.0); // 实际存储2.0(double精度问题) BigDecimal d2 = new BigDecimal("2.00"); System.out.println(d1.compareTo(d2)); // 可能返回非预期结果(建议始终用String构造) ``` #### 2. 零值处理 ```java BigDecimal zero1 = new BigDecimal("0.00"); BigDecimal zero2 = BigDecimal.ZERO; System.out.println(zero1.compareTo(zero2)); // 0(数值相等) ``` --- ### 七、最佳实践指南 1. **构造一致性**:始终使用`String`构造器避免精度丢失 2. **标度控制**:重要场景主动使用`setScale()`统一格式 3. **空值防御**:比较前进行`null`检查 ```java public static boolean safeCompare(BigDecimal a, BigDecimal b) { if(a == null || b == null) return false; return a.compareTo(b) == 0; } ``` --- ### 常见错误排查表 | 现象 | 原因分析 | 解决方案 | |----------------------|--------------------------|------------------------------| | 预期应相等但返回1/-1 | double构造导致精度丢失 | 改用String构造器 | | 返回结果不稳定 | 未处理尾随零 | 使用`stripTrailingZeros()` | | NPE异常 | 未做空值检查 | 添加null判断逻辑 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值