본문 바로가기
JAVA

JAVA 부동소수점 연산 [코딩백과 with JAVA]

by GangDev 2024. 12. 21.

부동소수점 연산

부동소수점 연산은 일반적인 산술 연산과는 달리 예측하기 어려운 결과를 낼 수 있는 특별한 세계라고 볼 수 있습니다. 이는 부동소수점의 내부 표현 방식과 연산 과정에서 발생하는 근사치 사용 때문입니다.

부동소수점 연산의 예상과 현실

다음 코드를 살펴보겠습니다:

double d1 = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1;
System.out.println("d1 == 1 ? " + (d1 == 1.0));

이 코드의 결과를 예상해보면, 대부분의 사람들은 true가 출력될 것으로 생각할 것입니다. 하지만 실제로는 false가 출력됩니다.

이는 부동소수점 추가 연산이 수행되고 반올림되는 방식 때문에 발생하는 현상입니다. 각 0.1의 덧셈마다 작은 오차가 누적되며, 최종적으로 1.0과 정확히 일치하지 않는 값이 됩니다.

부동소수점 연산의 원리

Java에서 부동소수점 연산은 IEEE 754 표준을 따릅니다. 이 표준은 부동소수점 수를 비트로 표현하는 방식을 정의합니다. 하지만 이 표준의 구체적인 내용은 이 책의 범위를 벗어나므로 자세한 설명은 생략하겠습니다.

부동소수점 연산의 주의사항

  1. 등식 비교 피하기:
// 피해야 할 코드
if (x == y) {
    // ...
}

// 권장되는 방법
public static boolean nearlyEqual(double a, double b, double epsilon) {
    final double diff = Math.abs(a - b);
    return diff <= epsilon;
}

if (nearlyEqual(d1, 1.0, 0.00001)) {
    // d1과 1.0이 거의 같다고 간주
}
  1. 반올림 오차 관리:
double total = 0.0;
for (int i = 0; i < 1000000; i++) {
    total += 0.000001;
}
System.out.println(total);
// 출력: 0.9999999999999989 (예상보다 작음)
  1. BigDecimal 사용:
import java.math.BigDecimal;

BigDecimal bdTotal = new BigDecimal("0.0");
for (int i = 0; i < 1000000; i++) {
    bdTotal = bdTotal.add(new BigDecimal("0.000001"));
}
System.out.println(bdTotal);
// 출력: 1.00000000000000000000
  1. 특수한 값 처리:
double result = Double.POSITIVE_INFINITY;
if (!Double.isFinite(result)) {
    System.out.println("무한대 또는 NaN");
}

부동소수점 연산 최적화 기법

  1. 중간 결과 저장: 연산 과정에서의 중간 결과를 저장하여 반올림 오차를 줄입니다.
  2. 정밀도 높은 타입 선택: 필요한 경우 double 대신 float를 사용하여 메모리를 절약할 수 있습니다.
  3. 알고리즘 선택: 가능한 경우 정수 연산을 사용하거나 고정소수점 연산을 고려해봅니다.

정리

부동소수점 연산은 강력하지만 예측하기 어려운 동작을 할 수 있습니다. 특히 금융 계산이나 과학적 계산 등 정확도가 중요한 분야에서는 더욱 주의가 필요합니다. 개발자는 이러한 특성을 이해하고 적절히 다루면 부동소수점 연산의 장점을 최대한 활용하면서도 잠재적인 문제를 회피할 수 있습니다.