컴퓨터의 소수(Decimal) 처리

60jong·2022년 8월 15일
0

CS

목록 보기
1/4
post-thumbnail

문제 상황

public class Decimal {
    public static void main(String[] args) {
        double a = 1.1;
        double b = 0.1;
        System.out.println(a+b==1.2); // false
    }
}

double형의 a(1.1)와 b(0.1)를 더하면 1.2가 나오지 않는 상황을 마주하게 된다.

먼저 컴퓨터의 소수 처리를 알아보자.

컴퓨터의 소수(Decimal) 처리

컴퓨터는 모든 수를 이진법으로 처리하는데, 소수(Decimal)는 어떻게 처리할까??

소수를 2진법으로

  1. 정수 부분을 2진법으로 나타낸다.
  1. 소수 부분에 2를 곱한다.
  1. 곱한 결과의 정수 부분을 취한다.
  1. 정수 부분을 떼고 남은 소수 부분이 0이 아니라면 2로 복귀한다.
ex) 15.125 > ????
1. 15 > 1111

2. 0.125 * 2 = '0'.25
3. 1111.0xxxx
4. 0.25 != 0

2. 0.25 * 2 = '0'.5
3. 1111.00xxx
4. 0.5 != 0

2. 0.5 * 2 = '1'.0
3. 1111.001xxx
4. 0 == 0


result : 15.125 > 1111.001

소수를 2진법으로 바꾸는 방식을 통해 1.1과 0.1을 2진법으로 표현해보자

  • 1.1 / 0.1
 1. 1 > 1
 2. 0.1 * 2 = '0'.2
 3. 1.0xxxx
 4. 0.2 != 0
 
 2. 0.2 * 2 = '0'.4
 3. 1.00xxxx
 4. 0.4 != 0
 
 2. 0.4 * 2 = '0'.8
 3. 1.000xxx
 4. 0.8 != 0
 
 2. 0.8 * 2 = '1'.6
 3. 1.0001xxx
 4. 0.6 != 0
 
 2. 0.6 * 2 = '1'.2
 3. 1.00011xxxxx
 4. 0.2 != 0                      ---------------------소숫점이 2,4,8,6,2,~~~ 무한히 순환하게 된다.

1.1을 2진법으로 변환하게 되면 1.0001100011000110001100011~~~, 소숫점이 '00011'이 순환하는 형태가 된다.

0.1을 2진법으로 변환하게 되면 0.0001100011000110001100011~~~, 소숫점이 '00011'이 순환하는 형태가 된다.

그렇다면 1.1 + 0.1 != 1.2가 발생하는 원인은 무엇일까.

Java - float, double

Java에는 실수를 저장할 수 있는 primitive type으로 float과 double이 존재한다.

  • float , 4byte -> 32 bit -> 1bit (Sign, 부호) + 8bit(Exponent, 지수) + 23bit(Mantissa, 가수)

  • double , 8byte -> 64 bit -> 1bit (S) + 11bit(E) + 52bit(M)


Java에서 double형으로 1.1과 0.1을 표현했다고 가정하자. 1.1과 0.1의 정수 부분은 손실없이 비트에 저장할 수 있지만, 무한히 순환하는 소수 부분은 52개의 비트에 밖에 저장할 수 없기에 실제로 저장된 값들은 1.1과 0.1과는 다를 수 밖에 없는 것이다.

따라서 소수 부분이 1을 2^n으로 나눈 결과가 아니라면 해당 소수를 2진법으로 나타냈을 때 소수 부분이 순환하게 되고, 이는 잘못된 연산을 이끌 수 있다.

실제 로직을 다룰 때 소숫점을 다루는 연산은 지양하는 것이 좋을 것 같다. 그 예로 1.4m 대신 140cm로 소숫점을 다루지 않는 것으로 하고, 소숫점을 다뤄야한다면, 위처럼 비트를 잘 생각하며 다뤄야겠다.

profile
울릉도에 별장 짓고 싶다

0개의 댓글