부동 소수점 연산

Tags:

제목 : 자바의 소수점 연산오류를 피할 수 있는 방법
글쓴이: 손님(guest) 2006/02/16 11:41:53 조회수:128 줄수:33

자바에서는 (int)(2.01 * 100) 가 201이 아니라 200 이 나옵니다.
c나 perl 데이타 베이스에서는 201 이라고 나오죠..
이것뿐만 아니라 단순히 100을 나누고 100을 곱하면 원래의 값과 맞지 않는 경우가 많습니다.

for (double i=0; i<1000; i++) {
	double b = i / 100 * 100;

	if (i != b) {
		out.println(i + ":" + b);
	}
}
&#91;/code&#93;

* 결과

7.0 : 7.000000000000001 
14.0 : 14.000000000000002 
28.0 : 28.000000000000004 
29.0 : 28.999999999999996 
55.0 : 55.00000000000001 
56.0 : 56.00000000000001 
57.0 : 56.99999999999999 
58.0 : 57.99999999999999 
109.0 : 109.00000000000001 
110.0 : 110.00000000000001 
111.0 : 111.00000000000001 
112.0 : 112.00000000000001 
113.0 : 112.99999999999999 
114.0 : 113.99999999999999 
115.0 : 114.9999999999999

이런 미세한 차이가 나는 원인이 무엇이며 어떻게 하면 이 오류를 피해갈 수 있을까요 ?
 
<span id="more-1283"></span>

제목 : Re: 소수점 연산은 항상 오류가..
글쓴이: 서민구(guest) 2006/02/19 22:56:43 조회수:3 줄수:90  
소수점 연산은 항상 오류가 발생합니다...

Java Puzzler 에 나온 예제를 하나 보여드리죠. 2달러를 내고 1달러 10센트를 돌려받았습니다. 그럼 물건 가격은 얼마일까요?

[code lang="java"]
public class Change {
    public static void main(String args[]) {
        System.out.println(2.00 - 1.10);
    }
}

이 프로그램으로는 답을 알 수 없습니다.

실행결과가,
0.90 은 아닙니다.
0.9 도 아닙니다.

기막히게도 0.8999999999999999 이죠.

해결하는 방법은 2가지입니다.

200-110 을 한 뒤 단위를 cent 로 출력하는 방법.
또는 BigDecimal을 쓰는 방법입니다.

보여주신 예중 하나를 실행해보면,

public class Test {
    public static void main(String[] args) {
        System.out.println(2.01 * 100);
    }
}

C:\WINDOWS\system32\cmd.exe /c java -cp . Test
200.99999999999997
Hit any key to close this window…

이렇게 결과가 나옵니다.

이유는 0.01 이 표현이 안되기 때문이죠.. 컴퓨터에서 부동 소수는 (1/2) * n1 + (1/4) * n2 + (1/8) * n3 + (1/16) * n4 … 과 같은 합으로 표현됩니다. 그리고 불행히도 0.01은 이런 값의 합으로 표현할 수가 없죠..

반면,
2.5 = 2 + 1/2,
2.25 = 2 + 1/4
와 같은 수는 표현이 가능해서 제대로 출력이 됩니다.

그런데 문제는 이것뿐만이 아니라… 컴퓨터에서의 부동소수는 또 작은 수의 범위는 촘촘하고, 큰 수의 범위는 띄엄띄엄합니다. 예를들어,

public class Test {
    public static void main(String[] args) {
        double i = 1.0e30;
        System.out.println( i == i + 1 );
    }
}

이 프로그램의 결과는 true입니다. 수가 커지면 수 간의 간격도 커져서 1 정도 더해서는 값에 변화도 생기지 않죠…

그래서 정확한 값을 쓰려면, BigDecimal을 쓰던가 아니면 단지 double은 추정된 값이라는 사실을 늘 기억하면 됩니다.. 어쨌든 double도 나름대로 실제 값이 근사하게 값을 주려고 노력하니까요.

또, BigDecimal을 쓰실 때 인자를 String으로 주어야하는데 유의해야 합니다. 예를들어,

import java.math.*;

public class Test {
    public static void main(String[] args) {
        BigDecimal b = new BigDecimal(2.01);
        BigDecimal b2 = new BigDecimal("2.01");

        System.out.println(b.toString());
        System.out.println(b2.toString());
    }
}

의 실행결과는

C:\WINDOWS\system32\cmd.exe /c java -cp . Test
2.0099999999999997868371792719699442386627197265625
2.01
Hit any key to close this window…

이죠.. 애초에 2.01 이라는 상수를 프로그램내에 쓰면 그 값 자체가 정확하게 안넘어옵니다. 따라서 문자열로 넘기셔야 합니다..

질문내용중에 (int) (2.01*100)이 C++/C에서는 제대로 된다고 하셨는데 이는 사실과 다릅니다.

#include

using namespace std;

int main()
{
cout << (int)(100 * 2.01) << endl; return EXIT_SUCCESS; } [/code] 결과는 200입니다. 2.01 * 100 해도 마찬가지구요.

Comments

One response to “부동 소수점 연산”

  1. 박현정 Avatar
    박현정

    감사합니다..~

Leave a Reply

Your email address will not be published. Required fields are marked *