나는 몰랐던 개발자 상식, 부동 소수점의 오류

얼마 전 유튜브를 보다가 부동 소수점의 오류 영상을 접하게 되었습니다. 궁금해서 다음 날 바로 실행해봤죠 그런데 웬걸 ?!?! 진짜로 값이 틀린 겁니다 !!!이번 내용은 우리가 프로그래밍을 하면서 알지 못하면 개고생할 수도 있는 바로 부동소수점 연산 오류입니다.
부동소수점부동소수점연산오류
avatar
2025.03.10
·
7 min read

얼마 전 유튜브를 보다가 부동 소수점의 오류 영상을 접하게 되었습니다. 궁금해서 다음 날 바로 실행해봤죠 그런데 웬걸 ?!?! 진짜로 값이 틀린 겁니다 !!!

이번 내용은 우리가 프로그래밍을 하면서 알지 못하면 개고생할 수도 있는 바로 부동소수점 연산 오류입니다.

"아니, 그냥 0.1 + 0.2 하면 0.3 나오는 거 아니야?"

"왜 자꾸 이상한 값이 나오지?"

저도 처음엔 이게 왜 그런지 몰랐습니다... 하지만 알고 나면 정말 단순한 원리입니다.

오늘은 부동소수점 오류가 왜 발생하는지, 그리고 어떻게 해결할 수 있는지 알아보겠습니다.


1. 부동소수점 연산 오류란?

컴퓨터는 숫자를 2진법(0과 1)으로 표현합니다. 그런데 우리가 흔히 사용하는 10진법 소수(0.1, 0.2 등)는 2진법으로 정확하게 표현되지 않는 경우가 많습니다.

예를 들어, 0.1을 2진법으로 변환하면 다음과 같이 무한 반복됩니다:

0.1 (10진수) = 0.000110011001100110011001100...(2진수, 무한 반복)

컴퓨터는 무한한 자릿수를 저장할 수 없기 때문에, 일정한 자리에서 잘라서 저장합니다.

이 과정에서 오차가 발생하게 됩니다.

그 결과, 우리가 0.1 + 0.2를 계산하면 0.30000000000000004 같은 이상한 값이 나오는 것입니다.

public class FloatingPointError {

    public static void main(String[] args) {

        System.out.println(0.1 + 0.2);  // 0.30000000000000004 출력

        System.out.println(1.1 + 2.2);  // 3.3000000000000003 출력

    }

}

이게 바로 부동소수점 연산 오류(Floating Point Precision Error) 입니다.


2. 부동소수점 오류 예제

다음은 실제 코드에서 부동소수점 오류가 발생하는 예제입니다.

1. 단순한 덧셈

public class FloatingPointTest {

    public static void main(String[] args) {

        double a = 0.1;

        double b = 0.2;

        System.out.println(a + b); // 0.30000000000000004

    }

}

2. 비교 연산에서 오류 발생

public class FloatingPointComparison {

    public static void main(String[] args) {

        double a = 0.1 + 0.2;

        double b = 0.3;

        

        if (a == b) {

            System.out.println("같다!");

        } else {

            System.out.println("다르다!");

        }

    }

}

출력 결과:

다르다!

이유는? 0.1 + 0.2의 실제 값이 0.3이 아니라 0.30000000000000004이기 때문입니다.


3. 부동소수점 오류 해결 방법

이제 문제를 해결하는 방법을 알아봅시다!

📚 1. Math.round() 함수 사용 (간단한 해결책)

Java에서는 Math.round()를 사용해서 특정 소수점 자리에서 반올림할 수 있습니다.

public class RoundExample {

    public static void main(String[] args) {

        double result = 0.1 + 0.2;

        System.out.println(Math.round(result * 100.0) / 100.0); // 0.3 출력

    }

}

하지만 Math.round()는 단순히 출력을 맞춰주는 것이기 때문에, 비교 연산에서는 여전히 문제가 발생할 수 있습니다.

📚 2. Math.abs()EPSILON을 사용한 비교 (비교 연산)

Java에서는 Math.abs()를 이용하여 두 수의 차이가 일정 범위 내에 있는지를 확인하는 방식으로 비교할 수 있습니다.

public class EpsilonComparison {

    public static void main(String[] args) {

        double a = 0.1 + 0.2;

        double b = 0.3;

        final double EPSILON = 1e-9; // 허용 오차 (매우 작은 값)

        if (Math.abs(a - b) < EPSILON) {

            System.out.println("a와 b는 같음!");

        } else {

            System.out.println("a와 b는 다름!");

        }

    }

}

📚 3. BigDecimal 사용 (가장 정확한 해결책)

가장 확실한 해결책은 BigDecimal을 사용하는 것입니다.

import java.math.BigDecimal;

public class BigDecimalExample {

    public static void main(String[] args) {

        BigDecimal a = new BigDecimal("0.1");

        BigDecimal b = new BigDecimal("0.2");

        

        System.out.println(a.add(b));  // 정확하게 0.3 출력

    }

}

BigDecimal을 사용하면 부동소수점 오류 없이 정확한 연산이 가능합니다!

하지만, BigDecimal을 사용할 때는 new BigDecimal("0.1")처럼 문자열로 초기화해야 합니다. new BigDecimal(0.1)처럼 double 값을 String으로 변환하여 넣으면

기존의 부동소수점 오차가 그대로 반영되므로 주의해야 합니다!


방법들을 정리를 해보면 !!

해결 방법

정확성

코드 복잡도

사용 예

Math.round()

X (단순 반올림)

Low (쉬움)

출력을 맞출 때

EPSILON을 활용한 비교

O (근사 비교)

Low (쉬움)

비교 연산할 때

BigDecimal

O (완벽한 정확도)

Middle (약간 복잡)

금융/정확한 계산

즉, 비교 연산을 할 땐 EPSILON을 활용하고, 정확한 계산이 필요하면 BigDecimal을 사용하는 것이 좋습니다

부동소수점 오류는 컴퓨터가 10진수를 2진수로 변환할 때 발생하는 필연적인 문제입니다.

이 개념을 모르고 있다가 실제로 자신을 부정할 수 있게 될 수도 있습니다 ㅎㅎ

하지만 이를 알고 있으면 충분히 해결할 수 있습니다!







- 컬렉션 아티클