1. 개요

이 사용방법(예제)에서는 Java에서 이중 값을 비교하는 다양한 방법에 대해 설명합니다. 특히 다른 원시 유형을 비교하는 것만큼 쉽지 않습니다. 사실 이것은 Java뿐만 아니라 다른 많은 언어에서도 문제가 됩니다.

먼저 간단한 == 연산자를 사용하는 것이 정확하지 않고 런타임에서 버그를 추적하기 어려울 수 있는 이유를 설명합니다. 그런 다음 일반 Java 및 일반적인 타사 라이브러리에서 double을 올바르게 비교하는 방법을 보여줍니다.

2. == 연산자 사용하기

== 연산자를 사용한 비교의 부정확성은 이중 값이 컴퓨터 메모리에 저장되는 방식으로 인해 발생합니다. 제한된 메모리 공간(보통 64비트)에 맞아야 하는 무한한 수의 값이 있음을 기억해야 합니다. 결과적으로 우리는 컴퓨터에서 대부분의 이중 값을 정확하게 표현할 수 없습니다 . 저장하려면 반올림해야 합니다 .

반올림이 부정확하기 때문에 다음과 같은 흥미로운 오류가 발생할 수 있습니다.

double d1 = 0;
for (int i = 1; i <= 8; i++) {
    d1 += 0.1;
 }

double d2 = 0.1 * 8;

System.out.println(d1);
System.out.println(d2);

두 변수 d1 과  d2는  모두 0.8과 같아야 합니다. 그러나 위의 코드를 실행하면 다음과 같은 결과가 표시됩니다.

0.7999999999999999
0.8

이 경우 두 값을 == 연산자와 비교하면 잘못된 결과가 생성됩니다. 이러한 이유로 더 복잡한 비교 알고리즘을 사용해야 합니다.

반올림 메커니즘에 대한 최고의 정밀도와 제어를 원하면 java.math.BigDecimal 클래스를 사용할 수 있습니다  .

3. 일반 자바에서 Double 비교하기

일반 Java에서 이중 값을 비교하기 위해 권장되는 알고리즘은 임계값 비교 방법 입니다. 이 경우 두 숫자의 차이가 일반적으로 엡실론 이라고 하는 지정된 허용오차 내에 있는지 확인해야 합니다 .

double epsilon = 0.000001d;

assertThat(Math.abs(d1 - d2) < epsilon).isTrue();

엡실론 값이 작을수록 비교 정확도가 높아집니다. 그러나 허용 오차 값을 너무 작게 지정하면 단순 == 비교에서와 같은 잘못된 결과를 얻게 됩니다. 일반적으로 엡실론의 소수점 이하 자릿수는 5와 6으로 시작하는 것이 좋습니다 .

불행히도 표준 JDK에는 권장되고 정확한 방법으로 이중 값을 비교하는 데 사용할 수 있는 유틸리티가 없습니다. 운 좋게도 우리는 직접 작성할 필요가 없습니다. 널리 알려진 무료 타사 라이브러리에서 제공하는 다양한 전용 방법을 사용할 수 있습니다.

4. Apache Commons Math 사용하기

Apache Commons Math 는 수학 및 통계 구성 요소 전용의 가장 큰 오픈 소스 라이브러리 중 하나입니다. 다양한 클래스와 메소드 중에서 특히 org.apache.commons.math3.util.Precision 클래스에 중점을 둘 것 입니다. 이중 값을 올바르게 비교하기 위한 2개의 유용한 equals() 메서드가 포함되어 있습니다 .

double epsilon = 0.000001d;

assertThat(Precision.equals(d1, d2, epsilon)).isTrue();
assertThat(Precision.equals(d1, d2)).isTrue();

여기서 사용 엡실론 변수는 앞의 예와 같은 의미를 갖는다. 허용되는 절대 오차의 양입니다. 그러나 임계값 알고리즘과의 유일한 유사성은 아닙니다. 특히 두 가지 모두 동일한 방법은 내부적으로 동일한 접근 방식을 사용합니다.

두 인수 함수 버전은  equals(d1, d2, 1)  메서드 호출 의 바로 가기일 뿐입니다 . 해당 버전에서 엡실론의 값은 상당히 높습니다. 그러므로 우리는 그것을 사용해서는 안되며 항상 우리 스스로 공차 값을 지정합니다.

5. 구아바 사용하기

Google의 Guava 는 표준 JDK 기능을 확장하는 핵심 Java 라이브러리의 큰 집합입니다. com.google.common.math 패키지 에 유용한 수학 유틸리티가 많이 포함되어 있습니다 . Guava에서 이중 값을 올바르게 비교하기 위해 DoubleMath 클래스 에서 fuzzyEquals() 메서드를  구현해 보겠습니다 .

double epsilon = 0.000001d;

assertThat(DoubleMath.fuzzyEquals(d1, d2, epsilon)).isTrue();

메서드 이름은 Apache Commons Math와 다르지만 내부적으로는 거의 동일하게 작동합니다. 유일한 차이점은 엡실론의 기본값으로 오버로드된 메서드가 없다는 것입니다.

6. JUnit 사용하기

JUnit 은 Java에서 가장 널리 사용되는 단위 테스트 프레임워크 중 하나입니다. 일반적으로 모든 단위 테스트는 일반적으로 예상 값과 실제 값의 차이를 분석하는 것으로 끝납니다. 따라서 테스트 프레임워크에는 정확하고 정확한 비교 알고리즘이 있어야 합니다. 사실, JUnit은 이중 값 평등을 확인하기 위한 전용 메소드를 포함하여 공통 객체, 컬렉션 및 기본 유형에 대한 일련의 비교 메소드를 제공합니다.

double epsilon = 0.000001d;
assertEquals(d1, d2, epsilon);

사실 앞에서 설명한 Guava 및 Apache Commons의 방법과 동일하게 작동합니다.

엡실론 인수가 없는 더 이상 사용되지 않는 두 개의 인수 버전도 있다는 점을 지적하는 것이 중요합니다. 그러나 결과가 항상 올바른지 확인하려면 세 개의 인수 버전을 사용해야 합니다.

7. 결론

이 기사에서는 Java에서 이중 값을 비교하는 다양한 방법을 살펴보았습니다.

간단한 비교가 런타임에서 버그를 추적하기 어렵게 만드는 이유를 설명했습니다. 그런 다음 일반 Java와 공용 라이브러리의 값을 올바르게 비교하는 방법을 보여주었습니다.

항상 그렇듯이 예제의 소스 코드는 GitHub 에서 찾을 수 있습니다 .

Junit footer banner