[java] BigDecimal의 equals 와 compareTo with kotlin
😲 서론
BigDecimal 을 통해 연산자를 통해 값을 비교하다가 조심해야될 것을 발견했다.
🫡 본론
결과의 값이 1인지를 확인하려고 했는데, 내 생각과 다르게 false가 나왔다.
fun main() {
val result = BigDecimal("1.00")
println(result == BigDecimal.ONE) // false
}
equals의 java doc 확인해보니 아래와 같은 설명이 적혀있었다.
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).
Params: x – Object to which this BigDecimal is to be compared.
Returns: true if and only if the specified Object is a BigDecimal whose value and scale are equal to this BigDecimal's.
See Also: compareTo(BigDecimal), hashCode
BigDecimal의 equals는 2.0과 2.00을 다르게 판단한다고 적혀있다.
해서 내가 원하는 1.00, 1 비교는 equals로 하면 안된다.
이 부분은 BigDecimal을 계산할 때 실수할 여지가 많은 부분으로 조심해서 비교해야될 부분으로 보인다.
그렇다면, 1.0과 1.00, 1 을 같게 비교하려면 어떤 걸 사용해야할까?
compareTo Javadoc
Compares this BigDecimal with the specified BigDecimal. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. This method is provided in preference to individual methods for each of the six boolean comparison operators (<, ==, >, >=, !=, <=). The suggested idiom for performing these comparisons is: (x.compareTo(y) <op> 0), where <op> is one of the six comparison operators.
Params: val – BigDecimal to which this BigDecimal is to be compared.
Returns: -1, 0, or 1 as this BigDecimal is numerically less than, equal to, or greater than val.
compareTo는 소숫점이 다른 2.0과 2.00을 같은 값으로 인지한다.
fun main() {
val result = BigDecimal("1.00")
println(result.compareTo(BigDecimal.ONE)) // 0
}
compareTo를 타는 >, >=, <, <= 와 같은 비교 연산자의 경우 사용해도 되지만,
==, !== 의 경우 equals를 타기 때문에 대부분의 경우에서는 compareTo()를 통해 0인지를 확인하자.
그렇다면 Java는 equals를 왜 2.0, 2.00을 다른 값으로 본 걸까?
stackoverflow에 바로 이해가 가는 예시가 있었다.
fun main() {
val a = BigDecimal("2.0")
val b = BigDecimal("2.00")
val three = BigDecimal(3)
println(a.divide(three, RoundingMode.HALF_UP)) // 0.7
println(b.divide(three, RoundingMode.HALF_UP)) // 0.67
}
등식의 일반적인 규칙은 두 개의 동일한 값을 서로 대입할 수 있어야 한다.
즉, 하나의 값을 사용하여 계산을 수행하면 어떤 결과가 나오는 경우 동일한 계산에 같은 값을 대입하면
첫 번째 결과와 동일한 결과가 나와야 한다.
이는 String, Integer, BigDecimal 등과 같은 값인 객체에 적용된다.
위 예시를 통해 결과적으로 a의 값은 b를 대체할 수 없으므로 a.equals(b)는 false여야 한다고 말한다.
🏄🏻♂️ 결론
equals의 정책은 이해가 됐다. 두 값은 엄밀히 따지면 다른 값으로 볼 수 있다.
하지만, 단순 비교를 위해서는 2.0이든 2.00이든, 2.0000이든 동일한 값으로 보고 싶은 경우가 많다.
해서 2.0, 2.00을 의식적으로 비교해야 하는 경우가 아니라면, compareTo를 사용해 비교하자.
참조
https://bugs.openjdk.org/browse/JDK-8261862
https://stackoverflow.com/questions/6787142/bigdecimal-equals-versus-compareto