[해결]C 언어 질문! double에서 floor()
2017.11.15 02:05
결론 -- 댓글에 많은 도움을 받았습니다. 감사합니다. 궁극적으로는 다른 언어로 옮겨가기로 결정했고, 이 문제는 일단 땜빵으로 돌아갔습니다.
컴퓨터가 이진법으로 계산을 하기때문에, 소수 계산을 할때 십진법으로는 도저히 이해가 안가는 엉뚱한 짓을 할 때가 있습니다. 지금 여쭙는 것이 그 중 하나인데, 원인은 알지만 해결책이 없나 해서 질문 올립니다.
문제의 소스코드:
#include<stdio.h>
#include<math.h>
void main()
{
double a;
a=1000.0*1.001;
printf("%g %g\n",a,floor(a));
}
요걸 cc -lm 해서 컴파일 한 다음 수행하면..
1001 1001
하고 출력하길 기대하는데.. 1001 1000 하고 출력 합니다. -_-;; 그래서.. 이 floor 에 문제가 있군 하고는..
#include<stdio.h>
void main()
{
double a;
a=1000.0*1.001;
printf("%g %d\n",a,(int)a));
}
해도 결과는 똑같습니다. -_-;; 추측하는 것은 저 0.001 이 이진수로 변환될때 무한 소수로 변환되어서.. 결과가 1001에 아주 쪼끔 모자라는 상황인 듯 한데요. 해결 방법이 없을까요. 웃기는건 float로 낮추면 문제없이 동작합니다. 그렇지만 제가 하는 일이 double precision이 필요한 일이라 답이 안되는듯 합니다. 뭔가 말도 안되는 실수를 했다기엔 너무 간단한 소스코드라.
코멘트 13
-
영진
11.15 03:25
-
왕초보
11.15 07:00
이 문제는 이미 explicit typecasting 까지 해서 해결을 시도해 보았는데 아무 차이가 없습니다. ㅠㅜ
-
piloteer
11.15 06:36
이 경우 한정으로는 (1000.0*1001.0)/1000.0 하시면 1001과 1000은 IEEE754기준 오차가 안 나므로 되긴 합니다만.. floor대신 반올림하실 방법을 찾는 게 좋을지도 모르겠네요.
-
왕초보
11.15 07:01
이 특정 케이스라면 어떻게든 workaround를 찾아볼텐데요.. 앞으로 이런 일을 많이 해야 하는데 general하게 피해가는 방법을 찾고 있습니다.
-
piloteer
11.15 07:15
1. 내림 대신 반올림 (floor(x+0.5))을 사용하는 것은 적합하지 않은 경우인가요?
2. 꼭 IEEE754를 쓰셔야 하나요? C언어의 경우 무슨 라이브러리를 쓰셔야 할지 잘 모르겠으나 소숫점도 정확하게 표현할 수 있는 자료형도 존재하기는 합니다만.. (대체로 느리고 더 변수 크기가 크거나 범위가 좁거나 하는 약점은 있습니다.)
-
왕초보
11.15 10:36
1. 사실 이 상황에서 floor 대신 round나 ceil을 쓴다고 문제가 해결될 것 같지 않습니다. -_-;;
2. 굳이 double을 써야하느냐.. 일단 float는 제대로 된 계산이 안되는 경우가 제법 있어서 논외고요.. long double이 있기는 한데 이걸 쓰기는 좀 뭐하고.. 이걸 하자고 새로운 자료형을 정의하는건 제 공력으론.. 피하고 싶습니다.
느린것은 별 문제가 안될듯 합니다만. 어떻게 BCD 연산을 할 수 없나까지 생각중입니다. 마이크로소프트 엑셀은 무슨 이유에선가 문제없이 동작합니다. 그래서 VBA로 갈까도 생각중입니다.
-
piloteer
11.15 11:02
딴 언어 쓰셔도 된다면 C#이나 파이썬 등은 decimal 타입이 있습니다. C도 비슷한 데이터타입이 나온 게 있을텐데 흔히 쓰는 라이브러리가 뭔지 모르겠네요. C는 일부 고속 DSP목적 빼고는 일부러 안 건드리고 있어서..
속도는 느린데 (연산에 754가속 명령어들을 못 쓰는 건 당연하고 파이썬같은 경우 decimal객체를 새로 만들 때 숫자가 double->decimal 순서로 캐스팅되는 걸 막기 위해 일부러 숫자를 문자열 형식으로 넣어줘야 하는 경우가 있습니다. Decimal ('1.01') 같은 느낌.) 뭐 요즘 컴퓨터가 워낙 좋으니 실시간 고속 데이터 처리같은 목적 아니면 쓸만합니다.
-
왕초보
11.16 04:03
아무래도 궁극적으론 말씀하신 대로 다른 언어로 옮겨가야 할 듯 합니다. 가게 되면 아마 파이톤으로 갈 듯 합니다. 감사합니다.
-
왕초보
11.15 10:38
일단 현재 workaround는 a-floor(a)가 1에 매우 가까우면 floor(a)에 1을 더해서 결과로 사용하는 걸로 만들어서 돌아가기는 하고, 결과가 제대로 나오긴 하는데.. 이렇게 까지 해야 하는 건지. ㄷㄷㄷ
-
하얀강아지
11.15 12:10
제가 이 방안을 말씀드리려 로그인했는데 이미 하셨네요.
이런 경우 그냥 편하게 오차값을 더해 해결했습니다.
CoPro 네가 오차를 무시하니 난 그만큼 오차를 더해주겠다 하는 거죠.
-
왕초보
11.16 04:05
ㅎㅎ
이번 경우는 일이 그리 크지 않아서 문제 난 곳을 금방 찾고, 열심히 디버깅하다가 이 질문을 하게 되었는데.. 일이 조금 커서 문제 난 곳을 못 찾고 그냥 넘어갔었다면 황당했을 수도 있겠다 싶습니다. ㅠㅜ
-
SYLPHY
11.15 13:36
비슷한 문제로 0.1+0.2도 0.3이 정확하게 안 나옵니다.
IEEE754는 granularity 문제가 심해서 정확한 연산을 할 때는 안 쓰는게 좋다고 알고 있습니다.. ^^;
다른 자료형을 쓰기 힘든 상황이신가요?
-
왕초보
11.16 04:02
ㅎㅎ 그리 어려운 문제를 풀려고 하는 것은 아니라서 아무 자료형이나 쓸 수만 있으면 쓰면 되는데 제가 아는게 많이 모자란 것이 문제의 원인이라면 원인이지요. 원래 프로그래머가 아닌지라. 제 프로그래밍은 학부 1학년때 배운게 끝입니다. ㄷㄷㄷ
a = 1000.0 * 1001 하면 정수계산을 해서 truncate이 일어나는거 아닌가요
a = 1000.0 * 1001.0 하시면 될듯요. 아시는거겠지만 ... ^^