solved]c 의 소숫점연산 대실망.
글쓴이: 송효진 / 작성시간: 토, 2008/06/14 - 10:35오후
#include <stdio.h> #include <math.h> int main() { float a = 2.3; float b = 100.0; printf("%d\n", (int)floor(2.3 * 100.0)); printf("%d\n", (int)floor(a * b)); return 0; } $ gcc test.c -o test -lm $ ./test 229 230
php 에서도 덩달아 같은꼴이 나버립니다.
근데 변수 메모리문제인지 무조건 버그결과만 나옵니다.
<?php $a = 2.3; $b = 100.0; printf("%d\n", (int)floor(2.3 * 100.0)); printf("%d\n", (int)floor($a * $b)); ?> $ php test.php 229 229
해결할 수 없는 문제라서 놔두는건가요?
그거 해결하는것이 cpu 자원 소모가 많이 되어서 그런건가요?
해결된 함수는 없나요?
Forums:
제 생각은...
제 생각은...
테스트는 안해봤습니다만...^^;
float형을 나타내도록 f를 붙여 주어야 하는 것 아닌가요?
이부분을
floor(2.3 * 100.0)
이렇게
floor(2.3f * 100.0f)
같은 code와 option으로 컴파일 해봤습니다.
229로 동일하게 나옵니다.
primewizard님인 언급한 것처럼 f를 붙이면 송효진님이 말씀하신 것처럼 나오는 군요.
ubuntu 8.04 32bit
gcc 4.2.3 (target i486-linux-gnu)
뭐가 버그라는 건지
뭐가 버그라는 건지 모르겠네요-_-; 실수는 부동소수점연산이니까 얼마든지 가능한 결과 아닌가요?
버그 아닙니다.
버그가 아니라 함수를 잘못 선택하신것 같습니다.
실수->정수 변환을 하려면 floor가 아니라 lround 를 써야 합니다.
lround() 는 반올림
lround() 는 반올림 아닌가요?
내림에서의 문제해결 방법은 아닌것 같은데요.
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
실수형은 정수형과 다르게 취급됩니다.
float(double) 100.0 is not equal to integer 100
우리가 일반적으로 사용하는 머신에서의 실수 의 값은 입력받은 값에 최대한 비슷한 값을 가지도록 되어 있습니다.
float 와 double 의 차이는 단순히 데이터 형의 크기가 다른것이 아니라, float 는 single-precision 으로 되어있고 double 는 double-precision 으로 구현되어 있기 때문에 데이터 형의 크기가 다른것으로 보이는 것입니다. 즉, float 보다 double 이 좀 더 정밀도가 높습니다만, 어디까지나 정수형에 최대한 가까운 값을 가질뿐, 정수값과 동일한 값을 갖을수는 없습니다.
이는 컴퓨터 아키텍쳐에 기반한 부분이라 어쩔 수 없습니다.
만약 int 100 과 float 100 을 같다고 처리하는 언어가 있다면, 이것은 그 언어만의 스타일이라고 볼 수 있고, 엄밀히 따지자면 잘못된 연산을 하고 있는것입니다.
-------------------------------------
김동수 - PublicEnemy
김동수 - Prototype for Evolution
다르다는 개념이
다르다는 개념이 '논리적인' 개념이지 '수학적인' 개념은 아니어야 하지 않나요?
이거 문제 있는것 아닌가요?
일관되지 못하잖아요?
------- 추가
ㅇㅋ
일관되네요 -_-;
'float 형은 나누어떨어지는 계산도 똑바로 해내지 못한다.'
땅땅땅.
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
float뿐만 아니라
float뿐만 아니라 double도 마찬가지입니다. 부동소수점에 근거해서 실수를 표현하는 이상, 부정확할수밖에 없습니다.
이부분에 대해서는 머신 입실론(machine epsilon)에 대한 내용을 읽어보시면 좀더 자세히 알수 있을것 같습니다.
이걸 버그라고 부른다면, 컴퓨터의 존재 그자체가 버그입니다.
물론 별도의 안전장치를 넣어서 예외처리를 해준다던가 할수도 있겠지만, 이부분은 연산성능과 trade-off이므로 그건 neversnow님의 말씀대로 언어나 그 라이브러리의 특징으로 봐야할 부분이지, 당연히 그러해야할 부분이 아닙니다.
변수가 포함된 곱셈과 상수끼리 곱셈하는 경우 차이
정수형과 달리 컴퓨터의 부동 소숫점 연산은 정확한 결과를 보장하지 않습니다.
단지 어느정도의 정확도만 보장해줄 뿐입니다.
심지어 0.0조차 0.0에 가까운 - 근사치값과 + 근사치값이 있습니다.
수치 해석과 같은 정밀한 실수 연산이 필요한 경우 별도의 라이브러리를 쓰게 됩니다.
// ubuntu 8.04 32bit
// gcc 4.2.3 (target i486-linux-gnu)
변수에 대한 곱셈은 gcc 경우 32bit multiply 명령 fmuls를 사용하는 것과 달리 상수들은 gcc 내부에서 컴파일시 미리 계산한 값을 사용합니다.(일종의 최적화?)
이때 처리하는 routine은 fmuls와 다른 routine을 사용하는 것으로 보입니다.
결국 실수 연산에서 일관성, 불일치 문제를 해결하려면 주의를 기울이는 수밖에 없습니다.
.....
neversnow 답변이 아주정확한데 글쓴이는 이해를 못하는겁니까?
인용 "다르다는 개념이 '논리적인' 개념이지 '수학적인' 개념은 아니어야 하지 않나요?"
그래서 뭐 어쩌라는건지...
---------------------------------------------------------------------------------------------------------------
루비 온 레일즈로 만들고 있는 홈페이지 입니다.
http://jihwankim.co.nr
여러 프로그램 소스들이 있습니다.
필요하신분은 받아가세요.
0 != 0.0 은 이해
0 != 0.0 은 이해 했지만
0 > 0.0 || 0 < 0.0 을 이해 못해서 질문했고,
나름대로 원래 정확하지 못하구나 하고 결론까지 내어 잘 적어놓은것 같은데,
왜 따지고 지랄이신가요?
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
ㅋㅋ
아니 뭐 이런분이 다있으신가...제가 말했듯 저분께서 친절하게 왜 그런가 설명을 해주셨잖아요
그 분이 말한것은 의견이 아니라 정확한 리퍼런스를 참고한 답변입니다.
그런데 님께서는 그분의 말꼬리를 물어잡고 따지셨는데요.
그걸 보고 저는 님께서 "아 이분은 말도안되는 말싸움을 즐기시는분이군아" 해서 글을 쓴겁니다.
제가 따진다구요? 지랄하고 있다구요?
님의 말투를 들어보면 자신이 이해하지못하는건 끝까지 말싸움으로 이끌어가는 지잘났다고 생각하는 고딩이거나 중딩같군요.
아니면 나이를 좀 드셨으면 말을좀 삼가할줄 아셨으면 좋겠습니다.
욕하면 기분이 좋습니까?
남을 욕하기 전에 자신이 뭘썼는지 읽어보세요.
이건뭐 트롤도 아니고 말이죠....
---------------------------------------------------------------------------------------------------------------
루비 온 레일즈로 만들고 있는 홈페이지 입니다.
http://jihwankim.co.nr
여러 프로그램 소스들이 있습니다.
필요하신분은 받아가세요.
아직 뭘
아직 뭘 잘못했는지를 모르시는군요.
따진다고요?
c 에서 그렇게 된것을 따지면 그렇지 않게 된답니까?
이해가 안되니까 재 질문한거잖아요.
'다른것을 다르다고 했는데... 더 어떻게 설명해야 할지 모르겠네요.'
정도만 했으면 좋았을것을
어쩌라고?
해버리면
저는 어쩌죠?
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
-_-
A: 1+1 이 뭔가요?
B: 2 입니다. 왜냐하면 어쩌고 저쩌고 중얼 중얼....
A: 왜 2 인가요 3 이나 4는 안되나요? "제가 생각하기론 왱알앵알"
C: 머 어쩌라는건지...
A: 왜 C 는 질알인가요 이해안되서 물어본게 잘못인가요?
C: Dun feed this troll....
---------------------------------------------------------------------------------------------------------------
루비 온 레일즈로 만들고 있는 홈페이지 입니다.
http://jihwankim.co.nr
여러 프로그램 소스들이 있습니다.
필요하신분은 받아가세요.
......탐구하지
......
탐구하지 않으면 실력은 늘지 않습니다...
------
자고 일어나서도 사과할 맘이 안드시면 더이상 답글 달지 마시고 잊으세요.
욕해서 죄송합니다.
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
그건 탐구가 아니라 말꼬리잡고 늘어지기라고 하죠.
아, 사과는 받아 들여 드리겠습니다. 저도 고딩때 님처럼 행동했던 적이 있었으니까 말이죠.
---------------------------------------------------------------------------------------------------------------
루비 온 레일즈로 만들고 있는 홈페이지 입니다.
http://jihwankim.co.nr
여러 프로그램 소스들이 있습니다.
필요하신분은 받아가세요.
floor말고 floorf
.
begin{signature}
THIS IS SPARTA!!!!!n.
end{signature}
float 형에서는 floorf를
float 형에서는 floorf를 쓰는 것 같습니다.
float a = 2.3f;
float b = 100.0f;
double c = 2.3;
double d = 100.0;
printf("%d\n", (int)floorf(a * b)); // 230
printf("%d\n", (int)floor(c * d)); // 229
그래도 229가 나오는데 이건 어쩔 수 없습니다.
begin{signature}
THIS IS SPARTA!!!!!n.
end{signature}
실망이시라면 이
실망이시라면 이 경우는, C언어에 대한 것이 아니라, IEEE 부동소수점 표현 표준에 대한 실망이어야할 것 같네요.
Orion Project : http://orionids.org
1. 부동소수점에 관한
1. 부동소수점에 관한 오해
'부정확하다'는 의미가 자리수가 많아지면 그제서야 대충 표현하는건줄 알았습니다.
그래서 겨우 230.0 인데 결과가 왜 이모양인가 에 대해 실망한거고요.
2. 오해에 따른 일관되지 못한 결과에 대한 불만
표현이 부정확하니 결과가 부정확한건데,
거기에 일관됨을 바랬습니다.
240.000015
240.000000
230.000000
230.000000 (여기는 229.99999999 쯤 될테지요)
이렇게 커졌다 작아졌다 하는것이 이해되지 않았습니다.
커지려면 커지고 작아지려면 작아져야지 '부정확해지는'것인줄 이해하지 못한거죠.
그러니까 0 != 0.0 인것이 아니고,
0 != 0.000??? 혹은 0 != -0.000??? 가 되는것이라는 것을 이해 했습니다.
3D 연산이 정수형인 이유에 속도 외에 이런점도 있을것 같다는 생각이 드네요.
모르면 찾아보고 질문하는것에 적극적이려 하는데,
왜 질문하냐 그러니 울컥했네요.
본 글타래는 이것으로 끝내고자 합니다.
답변달아주신 분들 감사합니다.
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
인용: 해결할 수 없는
이런 문제를 해결하기 위해 만들어진 다양한 라이브러리가 있습니다.
C에서는 GMP와 같은 라이브러리를 보시고요, PHP에서는 http://kr2.php.net/manual/en/language.types.float.php 를 참고하세요.
부동소숫점 연산은 Knuth 교수도 인정한 아주 어려운 분야라고 하더군요. C언어를 나무라는지는 마세요^^.
다들 중요한 부분은
다들 중요한 부분은 언급을 않 하시는 군요.. -_-
애시당초 이진법으로 십진법의 소수를 정확하게 표현 할 수 없습니다.
논리적인 문제가 아니라, 수학적인 문제 입니다. 컴퓨터의 한계 라기 보다, 2진법의 한계 입니다.
그렇지 않습니다.
그렇지 않습니다. 모든 진법은 수학적으로 완전히 동등합니다. 실수를 표현할때에 진법은 문제되지 않습니다.
10진법으로는
10진법으로는 유한소수인데 2진법으로는 무한소수가 될 수 있습니다.
그러므로 문제가 될 수도 있습니다.
(다만 이를 수학적인 한계라고 부르기는 어려운 것 같습니다.)
begin{signature}
THIS IS SPARTA!!!!!n.
end{signature}
3/10 은 10진법으로는
3/10 은 10진법으로는 유한소수 but 2진법으로는 무한소수
1/3 은 10진법으로는 무한소수 but 3진법으로는 유한소수
문제는.. 컴퓨터는 수를 나타내는 어떠한 "유한 크기의 표현 형태"를 취해야 하고,
그 것이 보통 32글자 2진법입니다. 그렇기 때문에 무한소수를 처리하는데 문제가 생기는 것이죠.
어, 근데 진법 변환 결과 나온 무한소수는 항상 순환소수이고, 유리수와 값이 일치하겠죠?
'부정확하다'에 대한
'부정확하다'에 대한 약간의 이해 향상.
1.0 / 2.0 은 0.5 로 정확히 나옵니다.
%.70f 정도 줘서 소수점을 확인해 보면 값이 정확함을 알 수 있습니다.
정확한건 아직 모르겠지만,
그냥 '수'가 아닌 '계산식'으로서 소수점을 표현하는것 같고,
계산은 그 '메모리범위'내에서 이루어지는듯 합니다.
곱셈이란 어차피 연속된 덧셈이고, (정수연산에서는 축약된 공식이 있을수도 있겠지만 실수연산에서는 못쓰겠죠)
그 덧셈과정에서 메모리범위를 넘어서 근사치표현이 되어버린거라고 이해했습니다.
위의 예제에서 보면,
소수점 앞뒤로 수가 있고, 자리수가 많은것 위주로 근사치가 되는것을 알 수 있습니다.
수가 큰게 문제가 아니고, 그 수를 표현하는 계산식이 복잡해지는게 문제일거라 생각되네요.
single precision 에서는 괜찮고 double precision 에서 문제가 될 수 있는것도,
두가지의 메모리 범위가 다르니 표현식도 달라지고,
표현방법이 달라지니 계산에 쓰이는 메모리범위도 다르겠죠.
많이 어렵네요;;;
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
printf("%.70f\n", 2.03 +
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
2진법을 확인하셔야...
부동소숫점 수는 10진법이 아니라 2진법으로 저장됩니다. 따라서 10진법으로 수십 자리를 찍어봤자 그건 그냥 printf 함수가 2진법으로 들어간 표현을 아주 긴 십진수로 고쳐서 보여주는 것일 뿐이지 "그 숫자의 표현 그 자체"와는 거리가 있습니다.
1.0/2.0은 이진수로 생각하면 1.0/10.0이고 그 답은 이진수 0.1이므로 당연히 정확하게 잘 나옵니다.
부동소숫점은 "계산식"이 아닌 "숫자"로 (어떻게 보느냐에 따라) 꽤 정확하게 숫자를 표현하며 숫자 a와 b로 무슨 연산을 했을 때 값이 뭐가 나와야 하는지 표준으로 아주 정확하게 정해져 있습니다. 결론만 말씀드리자면 "정확한 연산 결과, 이게 불가능할 때에는 부동소숫점 형식으로 정확히 표현 가능한 모든 숫자 중에서 가장 실제값에 가까운 숫자"가 나와야 합니다.
?
뭥미..
머리를 굴려라! 그래야 먹고 산다.
emerge
그러게요?
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
해당 아키텍쳐의
해당 아키텍쳐의 정밀도와도 관련이 있고용; 수의 표현 방법과 연산 방법에 따라서도 달라집니다.
예를 들어 고정 소수와 실수의 변환만 해도 오차가 생깁니다..
4.5를 XXXX.YYYY 형식의 고정 소수로 변환하는 방법 중 하나입니다.
round(4.5 * 2의4승) -> round(4.5 * 16) -> round(72) -> 72 -> 01001000 -> 4.5 (맞음)
3.7을 같은 고정 소수로 변환
round(3.7 * 2의4승) -> round(3.7 * 16) -> round(59.2) -> 59 -> 00111011 -> 3.6875 (틀려짐)
3.7이 3.6875로 변해버렸죵;
정수일때와 실수일 때는 곱셈, 나눗셈연산 하는 방법도 달라집니다 =_=; 관련 자료는 다른 분이 링크해 주실 듯 ㅋ;
고정 소수도 이렇게 정밀도에 문제가 있고요. 부동 소수는 더합니다; 더 큰 수를 표현하기 위해서 쓰이는데 부호(1비트), 지수(8비트), 가수(23비트)가 할당(IEEE)되기 땀시 ㄷㄷㄷ 물론 고정소수와 연산 방법도 다릅니다.
역시 관련 자료는 다음 분이 링크해 주실 겁니다;; 까먹어서;
관련되서 DBMS에
관련되서 DBMS에 double에 해당하는 실수형 데이터 타입이 있는데요. numeric같은걸로 대체되는 없는 DBMS도 있지만;
여튼 pgsql의 double precision 데이터 타입은 실수형과 매치됩니다.
그래서 SQL에서 > 100000.2344 와 >= 100000.2345 두가지 연산 결과에 100000.2345 가 포함 될 때가 있고 안 될때가 있습니다. 이것 때문에 애 먹은 기억이 있습니다. 답은 64비트 아키텍쳐로 오고 그에 상응하는 코드를 구현해서 써라 였고요.
실수의 정밀도 문제를 해결하기 위해서는 수학 전문 라이브러리를 쓰는 수 밖에는 없는것 같습니다. 그냥 해보는 것보다는 느릴테지만요 ㄷㄷ
ㄱㅅㄱㅅ PostgreSQL 은
ㄱㅅㄱㅅ
PostgreSQL 은 확장으로라도 뭔가 있을거라 믿습니다.
필요한 일은 없겠지만-_-;
emerge money
http://wiki.kldp.org/wiki.php/GentooInstallSimple - 명령어도 몇개 안되요~
http://xenosi.de/
https://xenosi.de/
당연히 numeric
당연히 numeric 타입에서는 문제가 없습니다 =)
2008년에 IEEE 754의 새
2008년에 IEEE 754의 새 버젼이 나왔나 보네요.
decimal format이 추가되었는데 decimal format을 사용하면
이 글타래의 문제점은 해결되지 않을까요?
http://en.wikipedia.org/wiki/IEEE_754
GCC 4.2에 decimal floating point를 지원한다는데... 더 알아 봐야 겠네요.
ㅇ
10년전 글이긴 한데.. 답글 달아봅니다.
(원글 작성자는 지금쯤 이불킥을 하고 계시겠지만..)
부동소수 반올림에 대해 "bankers rounding"을 한번 확인해 보세요.
댓글 달기