제목 : 자바의 소수점 연산오류를 피할 수 있는 방법
글쓴이: 손님(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); } } [/code] * 결과 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 해도 마찬가지구요.