컴파일러 버그인가? OS 버그인가? --;

mastercho의 이미지

학교에서 보안에 대해 수업을 받가가 HILL 알고리즘을 이용하여

문자를 암호화 시키는 프로그램을 짜도록 숙제를 내주셨습니다

그런데 문제를 해결하는 도중 신기한 문제점이 발겨되었습니다

->

Hill 알고리즘은 n행렬을 가지고 n의 문자와 매칭시켜

계산을 해 문자를 암호화 시키는것이고

복구할때는 n행렬의 역행열을 가지고 암호화 된 문자를 해석하는것입니다

여기서 중요한게 이러한 암호가 기법이 문제가 되는게 아니라

암호화 시킨것을 복구시킬때

아스키 코드값으로 100이면 분명 d가 출력되어야 하는데

c가 출력된다던가 아니면 다른 문자가 출력된다는것입니다

이 현상은 역행렬의 요소중 소수점값 즉 0.5 라든지 0.333이라든지
가 들어가면 발생한다는것이고

그렇지 않고 일반 정수로 역행렬이 나온다면

정상 출력된다는것입니다

원칙적으로
복구화된된 정수값 100을 cout<< (char) 100 <<endl ; 하면

분명 d가 출력되지만

암호에서 복구화된 정수값 100을 cout<< (char) 복구화된 100 <<endl;
을 하면 d가 안나오기도 합니다

한마디로
실수형을 계산해서 int 변수 넣으면
100이 나오는데 cout<<(char) 방법으로 출력하면
문자가 제대로 나오지 않는다는것이죠

소스도 한번 올려봅니다

그리고 실행해보시고 출력해보세요

우스운 이야기 일지 모르지만

리눅스 , 윈도우에서 출력되는 결과는 다르지만

똑같은 버그가 발생한것을 확인하였습니다

예 ) 키 배열로써
3 0 1
2 1 0
0 0 1

을 주었고

역행렬로써 값은 소수점값이 포함되어 있습니다
여기서 문자 abcde를 넣고

그리고 암호화 시켜지고 복구화 된 결과 값을 확인하면

분명 정수값으로 97 98 99 100 101 32
[위에 32값은 3X3행렬이기때문에 3문자씩 맞추기 위해 공백값으로 넣은것]

이 나오고 저 위대로 아스키 코드값이 나와야 한다면
그대로 abcde가 나와야 하는데
결과는 abcce라는 값이 나오는 황당함이 발생하는것이죠

재밌는것은 역행렬값이 정수로만 나왔고 그것을 계산해서 복구화 했을때는
정상 출력이 되는것입니다

그래도 중요한것 어차피 결과값 정수가 아스키 코드값과 매칭이 된다고
봤을적에 다르게 출력된다는게 말이 안되는거 같습니다

이 문제에 답을 해주실분을 찾습니다 ^^

File attachments: 
첨부파일 크기
파일 main.cpp8.01 KB
lunarainbow의 이미지

저는 C++은 몰라서 잘 해석이 안되는데..

이거 맞나요?

임의로 입력한 키값은 3 * 3 행렬이다. 그리고 암호화할 자료를 가져와 (어떻게 하는지 방법은 모르겠지만) 키값과 곱하기 연산을 한다.
후에 디코딩 할시엔 키값의 역행렬을 다시 곱해주면 원상 복구 된다.

맞나요??

그렇다면 어쩔 수 없는 결과인듯...;;

저의 짧은 지식으로 보았을 때,

( 3 * 3 ) * 0.3333333333333333333333 의 결과는,

정확한 3이 아닌, 2.99999999999999999999999~~ 인 것이라는거..

이건 int형으로 케스팅 될 시엔 2로 된다는거..

위와같이 0.33333~ 처럼 끝없이 내려가는 수 이외에도.

0.5 같이 사람이 보기엔 딱 떨어지는 숫자인 경우에도,

컴퓨터는 표현방법의 차이 때문에 아주 근소하게 차이가 나는 것으로 압니다.

예를 들어

0.499999999999999999999999999~
0.5000000000000000000000000000000~~~1

뭐 이렇게 말이죠.

예전에 암호화 소스를 봤었던걸 곰곰이 생각해보면,

테이블에 정수만 있었던것 같네요. ^^;;

그거 혹시 실수는 나오지 않도록 잘 만들어준 테이블 아닐까요??

소스가 C++임을 보곤 잠시 좌절한 후, 주절여 봤습니다.

mastercho의 이미지

아니요

분명 4.999를 정수 변수에 넣으면 4가 되지만

계산한 결과를 int 값에 넣고 체크한값이
결과는 분명 100이었고
그 변수를 바로
cout<<(char) 변수<<endl;

로 출력했을적에
99값인 c가 출력되었다는 것에 문제가 있습니다

제 소스를 컴파일 하시고 해보시면

테스트한 결과까지 보실수 있습니다

제가 처음 문제에서 제시한 키값배열과
이미 소스에서 제공하고 있는 정수만으로 된
역행렬로 된 키값배열를 가지고 테스트 해보시면

황당한 결과에 놀라실겁니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

lunarainbow의 이미지

음냐... 저는 C++을 몰라서.. 중간에 C소스로 넣구 해봤거든요.

확인 temp[i] : 97
[[[97]]]
확인 문자 : a
확인 temp[i] : 98
[[[98]]]
확인 문자 : b
확인 temp[i] : 99
[[[99]]]
확인 문자 : c
확인 temp[i] : 100
[[[99]]]
확인 문자 : c
확인 temp[i] : 101
[[[101]]]
확인 문자 : e
확인 temp[i] : 32
[[[32]]]
확인 문자 :
암호해독 완료(원본) : abcce

이런 결과가 나오네요.

[[ ]] <- 이거가 님이 출력한 환민 temp[i] 이거랑 같은 것을 출력한 것인데,

중간에 잼있는 결과가 나오네요.

제가 출력한 것에는 99로 나오는데, 님이 출력 하신거는 100으로 나오는... 황당한. ㅎㅎ

참고로 제가 출력할때 사용한것은..

printf("[[[%d]]]\n", (long long int)temp[i]);

이렇게 하였습니다. 굳이 long long int로 사용한것은 double이니깐.. ^^

아마.. 처음 추측했던데로, 실제론 99.9999999999~~ 이랬는데,

cout 이라는넘이 알아서 "이건 100으로 출력해야 할꺼야!" 하구 그래버린거 아닐까요?

그 증거로 다음처럼 해보세요

cout<<"확인 temp[i] : "<<(long long int)temp[i]<<endl;

다음과 같은 결과가 나옵니다

확인 temp[i] : 97
[[[97]]]
확인 문자 : a
확인 temp[i] : 98
[[[98]]]
확인 문자 : b
확인 temp[i] : 99
[[[99]]]
확인 문자 : c
확인 temp[i] : 99
[[[99]]]
확인 문자 : c
확인 temp[i] : 101
[[[101]]]
확인 문자 : e
확인 temp[i] : 32
[[[32]]]
확인 문자 :
mastercho의 이미지

리눅스에서는 일단 컴파일러가 long long int 라는것을 지원해서 인지 몰라도 lunarainbow님 말처럼 하면
출력결과가 바뀌기도 하네요 -_-;;

단 윈도우에서는 그대로 인데요?

제가 알기론 long long은 비표준인걸로 -_-;

그리고 그걸 떠나서 100으로 값이 표시 되었다면

그렇게 케스팅을 해주지 않는다고 해도

제대로 출력되어야 함이 정상이 아닌지요?

-_-;

lunarainbow님 답변은 도움이 됬지만 근본적인 문제의 원인을 알아냈다고 보기에는 무리가 좀 많은듯 하네요

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

neocoin의 이미지

http://doc.kldp.org/wiki.php/KLDPconf/20031011#s-3.3
의 전웅님께서 진행하신 세미나중에서 pt 18번째 변경사항에

언급되었습니다.

mastercho wrote:
리눅스에서는 일단 컴파일러가 long long int 라는것을 지원해서 인지 몰라도 lunarainbow님 말처럼 하면
출력결과가 바뀌기도 하네요 -_-;;

단 윈도우에서는 그대로 인데요?

제가 알기론 long long은 비표준인걸로 -_-;

그리고 그걸 떠나서 그렇게 케스팅을 해주지 않는다고 해도

제대로 출력되어야 함이 정상이 아닌지요?

-_-;

lunarainbow의 이미지

음.. 그러니깐 저가 말한것의 의미는,

cout 으로 출력하였을때 100으로 나온것이....

실제로는 double 형이니깐, 완벽히 100을 표현하지 못하고, 99.99999 의 근사한 차이가 났지 않았을까 하는 것입니다.

그런데 cout느 그걸 알아서 이건 100으로 표시해 줘야 할꺼야. 라고 판단한 뒤(너무너무 작은 차이니깐, 사용자의 일부러 의도하지 않았을꺼라고 판단하고..) 100으로 표시한 것이 아닐까 추측한 것입니다.

그런데 케릭터형으로 출력할때는, 어쩔 수 없이 정수형으로 변환해야 하니깐, 그 작은 차이일 지라도 무시할수 없기 때문에 어쩔 수 없이 99인 c를 출력한것이 아닐까요?

음.. 혹시 이런 테스트 하실 수 있으신지...

지금 그 소스를 CPU 종류가 다른 여러 컴퓨터에서 돌려보면,

아마 개중엔 100으로 나오는 것도 있지 않을까 싶네요.

부동소숫점 연산은 CPU 스팩(이라고 해야하나.. 용어를 잘 몰라서;; )에 따라 어떤곳에선 100.000000000~~~1 로 나올 수 있을 테니깐요..
(음. OS에 따라서도 영향을 받던가..?)

한가지 해보고 싶은거는, double형을 실수형으로 출력해 보고 싶네요.

그런데 double형이라 출력이 안되네요.;; 뭘로 써주는지 기억이 안나서 ^^;;

그렇게 했을때 정말 100으로 딱 떨어져 나올지 궁금합니다.

중간에 점 하나라도 찍힌다면, 제가 한 말이 맞을것입니다.

long long에 관해서는... 그럼 그냥 long int로 쓰면 될까요?

평소에 8byte 변수는 거의 쓰지 않아서, 잘 기억해 두지 않았서 모르겠네요. ^^;;

쓰고보니 저는 모르는거 투성이로군요 :wink:

mastercho의 이미지

제가 혹시나 해서 temp[i] 형을 int 형으로 선언을
다시 해봤습니다

int 형이라면 분명 9.9999999999999999가 나오더라도 9가 들어갈테죠?

하지만 결과는 같았습니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

lunarainbow의 이미지

번거로우시겠지만, 변환된 소스좀 올려주실 수 있나요?

mrchu의 이미지

int a = temp[i];
cout<<"확인 문자 : "<<a<<endl;

이렇게 출력 했더니,
확인 temp[i] : 97
확인 문자 : 97
확인 temp[i] : 98
확인 문자 : 98
확인 temp[i] : 99
확인 문자 : 99
확인 temp[i] : 100
확인 문자 : 99
확인 temp[i] : 101
확인 문자 : 101
확인 temp[i] : 32
확인 문자 : 32

이렇게 99라고 잘려서 나오는군요. cout이 반올림을 한 모양인데요?
아시다 시피 캐스팅 할때는 반올림이 아니고, 버림을 수행 하고요.
윈도우에서 VC6으로 테스트 했습니다.
mastercho의 이미지

다시 체크해보니까 아무래도

cout에 문제가 있나 싶습니다

temp[i] 형을 double에서 int형으로 바꿨을때 분명

double했을때와 같은 결과가 나왔었는데 -_-;

지금 보니 제가 착각했나보네요
아무래도 9.999999999 이게 맞는거 같습니다

단지 cout에서 double형을 표현할때 무한데 0.99999999가 있다면 반올림 해주는게 아닌가 싶네요 -_-;;;

cout에 대한 정확한 스펙을 모르니 , 답답하네요

어째튼 어느정도 원인을 감잡았습니다

답변 감사하고요

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

mrchu의 이미지

double temp[MAXROW];

float temp[MAXROW];
로 바꾸어도 제대로 나오는것 같더군요.
수학 프로그래밍 관련 책에서 그쪽 문제를 본것 같기도 하군요.

ifyou의 이미지

	double dd=99.9999999999;
	cout<<"dd:"<<dd<<endl;
	int ii=dd;
	cout<<"ii:"<<ii<<endl;

dd:100
ii:99

그냥 해봤는데 그렇게 나오네요..
99.99999999를 더 많이 하니까 ii도 100이 나오고..
비절씨뿔뿔 6.0결과..

바빠서 이만~

mastercho의 이미지

추측이지만 기계에서 0.1을 표현하지 못하는 단점때문에

그런식의 소수점이 나오면 기계에서 알아서 추측하고 그렇게 해버린게 아닌가 의심 가네요 ^^

이거에 대해 정확한 스펙이 공개가 되어있는 자료가 없는지 -_-;

이럴땐

전웅님이나 방준영님이 아쉽네요 :(

Quote:

코드:

double dd=99.9999999999;
cout<<"dd:"<<dd<<endl;
int ii=dd;
cout<<"ii:"<<ii<<endl;

dd:100
ii:99

그냥 해봤는데 그렇게 나오네요..
99.99999999를 더 많이 하니까 ii도 100이 나오고..

99.9999
에서 9를 많이 쓰면 결국 10이 들어가는걸 보면
제가 착각한건 아닌거 같습니다 -_-;

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

lunarainbow의 이미지

ㅎㅎ

저도 유명한 여러 고수님들 또는 재야의 숨은 고수님들이 등장하셔서 시원하게 해결해 주시길 바라고 있었는데..

mastercho의 이미지

추측이지만 -_-; double형의 소수점 자리에서 오버플로우가 날정도의 긴 소수점이라면
맨 뒷자리에서 반올림하는게 아닌가 추측을 해봅니다

근데 double형은 소수점을 몇짜리까지 표시할수 있었는지요?

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

singlet의 이미지

확인 temp[i] : 99
확인 문자 : c
확인 temp[i] : 99.999999999999986
확인 문자 : c
확인 temp[i] : 101.00000000000001
확인 문자 : e

설명은 필요없겠지요? ^^; 원하시는 결과는 반올림하시면 얻을 수 있겠네요. 코드가 반드시 양수라고 가정하면 + 0.5 하시고 캐스팅하는 걸로 끝나겠군요.

(추가: 제 참고자료에는 double 의 경우 정밀도가 십진법으로 15-17 자리라고 나와있습니다.)

mastercho의 이미지

그렇다면 오버플로우가 나면 반올림을 하는게 맞는거 같다는 생각이 듭니다

99.999 에서 9가 15개 이상일때

100이 되는걸 보니까요......

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

lunarainbow의 이미지

. double은 5.0*10^-324에서 1.7*10^308 사이의 값들을 15에서 16자리의 정밀도로 표현가능합니다

http://www.google.co.kr/search?q=cache:phth0IuUUKIJ:www.keyis.pe.kr/lectures_C/lecture2_2.htm+double+%ED%91%9C%ED%98%84%EB%B2%94%EC%9C%84&hl=ko&ie=UTF-8&inlang=ko

그냥 대충 찾았더니 이렇게 나오네요.

위에서 말씀하신건 유효자리인가 보네요..
(근데 유효자리는 뭐지?..;; 믿을만한 자리수라는 용어인가?)

vacancy의 이미지

오버플로라는 용어는 맞지 않는 것 같고요.

컴퓨터가 10진법을 사용하지 않기 때문에 발생하는 문제 같네요.
실제로 M$ Excel 사용자들이 크게 문제삼은 적도 있고요.
사실 M$의 문제라기보다 아키텍처상의 문제입니다만.

자세한 사항은 IEEE754 표준을 참고하시면 도움이 되잖을까 싶네요.
single/double-precision floating-point에 대한 표준이고요.
웬만한 컴퓨터 아키텍처 책에는 있는 걸로 알고 있습니다.

lunarainbow의 이미지

음.. 지금 궁금한것은, float가 어떻게 해석되느냐가 아닌,

정말 사용자의 의도를 어느정도 추측하고자 하기 위하여 너무 작은 차이는 어느정도 보정해 주고 있는 것일까 하는 것같습니다..;;

어쨌든 float/double은 자기 할일만큼만 하였을텐데, 그걸 해석하는 부분에서 어느정도까지 해석을 하는 것일까... 가 말입니다. :wink:

vacancy의 이미지

해석이 중요하지 않을리가요. -_-;

구조를 보시면 아시겠지만,
반올림도 10진이 아닌 2진 반올림을 해야합니다.

이 경우에 적용이 되는진 확인을 못해봤습니다만,
직접 floating-point값을 bit단위로 출력해서 확인해보시면
가장 확실할 것 같네요.

mastercho의 이미지

제가 허접해서 그러는데 확인하는 방법좀 일러주시면 감사하겠습니다

2진 반올림이라는것도 사실 이해가 잘 안가서요

코드로써 보여주시면 더욱 감사할거 같네요

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

vacancy의 이미지

void printdoublebybit(double x) {
  char *p_x;
  int lp1, lp2;

  p_x = (char *)(&x);

  for (lp1=7; lp1 >= 0; lp1--)
    for (lp2=7; lp2 >= 0; lp2--)
      printf("%d", (p_x[lp1] >> lp2) & 1);
  printf("\n");
}

제대로 만든건지 모르겠는데 -_-a
이걸로 temp[i]를 찍어보면
메모리에 해당 값이 어떻게 들어가있는지 알수 있습니다.
각 비트를 IEEE754 double-precision format에 맞춰보면,
100이 실제로는 메모리에 정확히 100으로 들어가있지 않다는걸
눈으로 확인하실수 있고요.

지금 보니 cout이 폭 제한때문인지 적절한 곳에서 반올림하는것 같네요.
그런데 significand bit 뒷부분이 쭉 1이라서,
어디서 반올림해도 100으로 잡히겠군요.

mastercho의 이미지

네 그렇네요
사실 2진 반올림이라는것이 1만 있어도 반올림된다는 의미인데 -_-; 그게 좀 헷갈리긴 했습니다

하지만 여기 질문에 참여하신분들도 일단 이런식?으로 반올림 된다는 사실을
정확히 알고 계신분은 없는걸로도 보이고요

어차피 9999999 무한데가
2진수로 11111111111111111 이었을테니

문제는 cout의 이동작이 표준인가 의문이네요

보통 표준이라면 int로 들어갈때 소수점 자리는
짤린다고 알고 있지 않습니다...

그런데 int 형에 값을 늘때 소수 15짜리 이상이
전부 99.9999999999 에서 9로 채워졌을 경우 100으로 들어갑니다

표준적인 동작으로 보자면 int형 변수에 99.99999999999999999999999999 무한데를 집어넣는다고 해도 99가 되어가야 된다고 알고 있었는데

여기서 체크를 해보니
반올림해서 값을 넣는 경우가 발생하지 않습니까?

여기서 표준에 대한 의문이 생기는데
모든 기계에서 같은 방법으로 동작하는지 아니면 intel 컴퓨터에서만
이러는건지 C에서 표준으로 정의가 되어 있는지 궁금하네요

이건 기본 C책에 나오는 double과 int형에 대한 내용을
다시 써야 할정도로 좀 충격적이지 않나 싶네요

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

BL의 이미지

http://cch.loria.fr/documentation/IEEE754/numerical_comp_guide/ncg_math.doc.html#405

참고하세요. 저도 자세히는 안봐서 설명해 드리지는 못하지만 여기에 답은 나와 있으리라 생각합니다. :)

mastercho의 이미지

:(

링크로 가봤는데 " 페이지를 표시할 수 없습니다. "라는 메세지가....

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

vacancy의 이미지

KT망이 이상해서 ip대역에 따라
저기 접속이 안되기도 하는것 같네요. -_-;

어쨌든 제가 지금까지 문제를 잘못보고 있었네요.
전 cout에 casting이 있는데도 문제가 있는건가 했더니,
casting도 없이 찍은거네요.
99.9999~ 가 100으로 찍히는 이유는,
아주 단순히 cout이 반올림을 하기 때문입니다.
찍힌 100은 정수 100이 아니고 반올림된 실수 100인 것이고요.
cout이 정수로 casting해서 찍은 것이 아닙니다. -_-;

The C++ Programming Language Section 21.4.3 (629page)를 보면,

Quote:
Note that floating-point values are rounded rather than just trancated and .. (후략)

.. 라고 되어 있습니다.

asteroid의 이미지

double d1 = 9.99999999;
double d2 = 10.0;

이코드의 어셈블리 코드를 보면, 두 변수에 같은 값(10.0)이 들어갑니다.

이 걸로 일단 컴파일러에서 오차를 보정해준다는 것을 알 수 있고,

x86 floating unit의 기본적인 rounding condition이 truncate이긴하지만,

gcc에서 만들어낸 코드를 보면 round up으로 condition을 변경하는 부분을

본 것 같고(이 부분은 다른분께서 확인좀 해주세요), 이렇게 rounding 정책이

바뀌어진 상태에서 저것과 유사한 경우가 발생한다면,

9.999999999 -> 10.0이 되는 경우또한 발생할 것 같은데요.

그리고 BL님이 올려주신 URL이 연결안되신다면, 구글캐시를 쓰는 방법이 있습니다.

http://www.google.co.kr/search?q=cache:HnBzSAKV2t4J:cch.loria.fr/documentation/IEEE754/numerical_comp_guide/ncg_math.doc.html

singlet의 이미지

* 99.999999999999999999999...(무한대) 따위는 컴퓨터에 없습니다아. 머리에서 지우세요.
(그나저나, 설령 있을 수 있다고 해도 저거 수학적으로 정확히 100 이에요 100.)

* 그건 그렇고, 예를 들어 int i = 99.999999999999999999999999999; 따위를 시도하면, 보신 대로 i 에는 역시 100 이 들어갈 수 있습니다. 왜냐면, 99.9999999999999... 가 먼저 double 타입의 상수가 되는데, 이 과정에서 반올림되어 100.0 이 되기 때문입니다. 이미 100.0 이 되어 버린 double 타입 상수를 i 에 대입하면 당연히 100 이 되지요. C 는 확실히 모르겠습니다만 C++98 2.13.3.1 에는

Quote:
If the scaled value is in the range of representable values for its type, the result is the scaled value if representable, else the larger or smaller representable value nearest the scaled value, chosen in an implementationdefined manner.

라고 하여 이런 선택을 허용하고 있습니다. 이건 부동 소수점 상수의 해석에 관련된 문제지 캐스팅하고는 관련 없으니 헷갈리지 마세요.

* cout 에서 100 을 출력하는 것도 캐스팅하고는 아무 관계 없습니다. 이런 일을 가지고 int 와 double 의 관계를 다시 써야 할 정도로 표준화 위원회가 허술할 리는 절대로 절대로 없지요. -_-; 저건 단지 cout 이 N 번째 자리수에서 반올림하니까 100.00... 이 나온 걸 보고 가볍게 .00... 을 버려버린 것뿐입니다. 출력은 정수처럼 됐지만 저건 엄연히 실수입니다. 캐스팅은 아예 일어나지도 않습니다.

mastercho의 이미지

singlet님 말씀에서 보면 일단
표준에서 정의하길 구현맘이라는것이라는것을 이해는 했습니다만은 거기까지는 좋았는데

컴퓨터에 0.99999999999999999999 뭐 이런것은
존재하지 않는다고 하시니 쪼금 그렇네요

수학적으로 무한데값으로 갔을적에 그게 0.99999999999999999가 1 이 된다는것은

고등학교 나온 사람이라면 다 아는 내용이고 굳이 상기해주실 필요는 없습니다

비유를 하자면 그렇다는것이었지 여기서 무슨 수학을 논하자고 할려는건 좀 그렇네요, 분명 제가 무슨 말을 하려는지 이해를 하셨으라 보는데..... 쩝

그리고 문제는 본질은.....
말씀대로 0.99999999999999 이런게 없다치더라도
어째튼 부동소수점을 표현할수 있는 부분을 초과시
반올림된다는 사실이 중요한거 아니겠습니까?

그거로 인해 저런 0.9999999 스타일의 값을 넣었을 경우 , 예기치 않은 1이 나올수 있는거고요

어셈블리로 확인해 주신분도 있지만 어셈블리나 C의 동작이 명확하지 않은데서 뭔가 의문은 충분히 남을수 있는 것이라 보는데...
너무 단정을 지으시는건지 모르겠습니다

위에 어셈블리 코드만 보더라도 소수점이 8개에 불구함에도 반올림 계산을 하는것을 볼수 있습니다

double형의 소수점 표현 한계에 도달하지 않더라도 말이죠

일반적으로 어셈이나 뭐 C책을 봤을적에는 [소수점은 버리는 방식]

double형의 동작을 제대로 설명해주지 않아서 , 많은 오해를

부르고 있는게 아닌가 싶습니다 , 구현맘이라는것도

사실 애매하기 짝이 없고요

충분히 토의해 볼것이 더 있다고 생각이 됩니다

참 cout에 대한 이야기는 조금 엇나간거 같네요
cout에서 정밀도를 설정해주지 않는 한
정수값으로 표시를 해줍니다

이건 문제의 본질에서 그리 중요한 이야기는 아니라 보고요

어째튼 좋은 이야기가 더 나와주었으면 좋겠네요 :)

Quote:
Most of the time, floating-point results are rounded:

computed result = (true result) ± roundoff

In IEEE arithmetic, with rounding mode to nearest,

0 roundoff 1/2 ulp

of the computed result.
ulp is an acronym for Unit in the Last Place. The least significant bit of the fraction of a number in its standard representation, is the last place. If the roundoff error is less than or equal to one half unit in the last place, then the calculation is correctly rounded.

여기에 나온게 round에 대한 방법을 말한거 같다는 생각이드네요

여기에 따르면 roundoff ulp가 1/2이라는게 보이는데
위에 말씀하신 구현 맘이라는것도 약간 어폐가 있어보이네요

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

mrchu의 이미지

C/C++ 언어에서 부동 소수점 연산결과에 대해 검사하는 책임은 컴파일러에 있는것이 아니고, 프로그래머에게 있는것으로 알고 있습니다.

이 글에 대한 결론은, 컴파일러/OS/라이브러리 어느곳에도 버그는 없었다는 것이 결론이 될 것 같습니다.

정말로 수학적으로 정밀도가 검증된 연산이 필요하다면, BCD등의 10진 연산을 수행 하거나, 수학 라이브러리를 사용 하거나, 매쓰매티카, 맷랩 같은도구를 사용 하는 수밖에 없지 않을까요?

dg의 이미지

long long 은 표준입니다..
언제부터 표준에 들어갔는지는 모르겠지만..
C99(ISO/IEC 9899:1999)에 들어가 있네요..
vc6.0에서는 안지원하는데 long long 대신에 __int64 쓰면 됩니다.

죠커의 이미지

mastercho wrote:
:(

링크로 가봤는데 " 페이지를 표시할 수 없습니다. "라는 메세지가....

구글을 쓰세요 :-)

http://www.google.co.kr/search?q=cache:HnBzSAKV2t4J:cch.loria.fr/documentation/IEEE754/numerical_comp_guide/ncg_math.doc.html+&hl=ko&ie=UTF-8

죠커의 이미지

부동소수점은 10진수로 변환하는 과정에서 반올림 없이 변환될수 있는 수는 표준에 의해서 float, double, long double은 6, 10, 10 이상의 자리수를 보장해야 한다고 나와있습니다. 실제로 확인해보고 싶으면 아래의 소스를 실행시켜 보십시요.

#include <iostream>
#include <cfloat>

using namespace std;

main()
{
    cout << "float의 10진 변환 한계: " << FLT_DIG << endl;
    cout << "double의 10진 변환 한계: " << DBL_DIG << endl;
    cout << "long double의 10진 변환 한계: " << LDBL_DIG << endl;
    return 0;
}

6, 15, 15가 나올겁니다. 10진수 변환 과정에서 오차가 생겨도 double에서 15자리까지는 보장이 된다는 거죠.

그런데 cout을 쓰던 printf를 쓰던 소수점 자리수가 저만큼 못 쓸겁니다. 그것은 cout이나 printf의 한계가 아닙니다. 옵션을 바꿔주시면 됩니다.

.20f 나 cout << precision (20)등을 이용하시면 반 올림 되는 부분이 더 줄어드신것을 보실수 있을 겁니다. 이런 옵션을 넣지 않는다면 printf나 cout은 float형만 유효숫자의 자리수 맞춰서 출력해줄겁니다. 아마도 디폴트는 5-6자리 밖에 보장을 못해줄겁니다. :-P

지금 시간이 촉박하여 원본의 소스는 확인해보지 못했습니다만 논쟁의 중점이 된것은 double의 15자리까지 반올림없이 보장하는 특성이 아니라 printf나 cout의 특성인것 같아서 날림 답변을 달아봅니다. 9.99999999 정도는 반올림 되지 않고 double이 소화해냅니다. 숫자가 오차가 생기는 것은 어쩔수 없지만요.

#include <iostream>
#include <iomanip>

using namespace std;

main()
{
    double d1 = 9.99999999999999;
    double d2 = 9.9999999999999999;
    printf("%.16f\t%.16f\n", d1, d2);
    cout << setprecision (20) << d1 << " // " << d2 << endl;

    return 0;
}
mastercho의 이미지

네..... 제가 공부나 프로젝트를 하면서 double 쓸일이 없었다보니까 , 제 밑바닥한계를 많이 보인거 같습니다 ^^

C책 본지도 좀 오래되었고요 :( , 너무 기본에 충실하지 못해서 이런 -_-; 황당한 결과에 충격을 ~~

대충 문서를 보면
double이 어차피 정확한 수치를 나타내지 못하기때문에 그나마 정확한값으로
조정할려고 반올림으로 그걸 보완하는 시도하는거였더라고요
그게 당연한거라고 나오고.......

어째튼
위에 답변 달아 주신분 감사드립니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

singlet의 이미지

mastercho wrote:
어째튼 부동소수점을 표현할수 있는 부분을 초과시
반올림된다는 사실이 중요한거 아니겠습니까?

다시 말씀드리지만 반올림이 아니라 구현 마음대로입니다. :( 구현 맘이라는 건 반올림된다는 보장도 없다는 겁니다.

mastercho wrote:
어셈블리로 확인해 주신분도 있지만 어셈블리나 C의 동작이 명확하지 않은데서 뭔가 의문은 충분히 남을수 있는 것이라 보는데...
너무 단정을 지으시는건지 모르겠습니다

원래 표준은 모든 동작을 명확히 지정하지는 않습니다. 많은 부분이 implementation 에게 떠넘겨집니다. 심지어 -5 % 2 조차도 C90 이나 C++98 에서는 implementation-defined 입니다. 이건 의도적으로 그렇게 남겨둔 것이지 뭐가 잘못돼서 애매하게 남아 있는 게 아닙니다. 의도적으로 그렇게 돼 있는 표준을 인용한 게 왜 너무 단정짓는 게 되는 건지는 이해가 가질 않는군요. :(

mastercho wrote:
위에 어셈블리 코드만 보더라도 소수점이 8개에 불구함에도 반올림 계산을 하는것을 볼수 있습니다. double형의 소수점 표현 한계에 도달하지 않더라도 말이죠

CN 님의 답변대로, 저건 절대 반올림돼서는 안 됩니다. 무언가가 분명 잘못된 겁니다.

mastercho wrote:
참 cout에 대한 이야기는 조금 엇나간거 같네요
cout에서 정밀도를 설정해주지 않는 한
정수값으로 표시를 해줍니다

이건 문제의 본질에서 그리 중요한 이야기는 아니라 보고요

제시하신 원 소스에는, 사실은 부동소수점 상수 문제도 부동소수점 결과의 반올림으로 인해 발생하는 문제도 존재하지 않습니다. 문제 자체가 cout 의 출력을 잘못 이해하고 있어서 발생하는 것인데 중요하지 않을 리가 있습니까. :(

mastercho wrote:
Quote:
Most of the time, floating-point results are rounded:

computed result = (true result) ± roundoff

In IEEE arithmetic, with rounding mode to nearest,

0 roundoff 1/2 ulp

of the computed result.
ulp is an acronym for Unit in the Last Place. The least significant bit of the fraction of a number in its standard representation, is the last place. If the roundoff error is less than or equal to one half unit in the last place, then the calculation is correctly rounded.

여기에 나온게 round에 대한 방법을 말한거 같다는 생각이드네요

여기에 따르면 roundoff ulp가 1/2이라는게 보이는데
위에 말씀하신 구현 맘이라는것도 약간 어폐가 있어보이네요


이건 floating-point results 에 관한 얘깁니다. 산술 연산 결과에 대한 얘기는 제가 적어둔 부동 소수점 상수의 해석과는 또다른 것이고 어폐는 전혀 없습니다. :(
vacancy의 이미지

#include <stdio.h>

int main() {
  double d1 = 9.999999999999999;
  double d2 = 10.;

  printf("d1 = %.20f\n", d1);
  printf("d2 = %.20f\n", d2);

  return 0;
}

$ ./a
d1 = 9.99999999999999822364
d2 = 10.00000000000000000000
vacancy의 이미지

#include <iostream>

using namespace std;

int main() {
  double d1 = 9.999999999999999;
  double d2 = 10.;

  cout.precision(30);
  cout << "d1 = " << d1 << endl;
  cout << "d2 = " << d2 << endl;

  return 0;
}

d1 = 9.9999999999999982
d2 = 10
vacancy의 이미지

위의 것들은 그냥 참고하시라고 올린 것이고요.
지금 상황에서 더 무엇이 궁금하신지 잘 모르겠네요. -_-a

cout 에서 출력한 값은 적당한 자릿수에서 반올림한 결과고요.
그래서 100이 나온 것이고요. 이 값은 정수 100이 아니고 실수 100입니다.
"어 99.999~를 정수로 찍으니 100이 되네."가 아니라,
cout이 적당한 자릿수에 맞게 출력되도록 적당한 곳에서 반올림하여
실수 100을 찍은 것에 불과합니다.
88.888~ 을 찍어보시면 잘못 이해하고 계시다는 걸 알수 있습니다.
( cout이 설정 없으면 정수로 찍는다고 하셨는데, 그렇지 않습니다. -_-a )

그 이후 char로 casting해서 찍을 때는,
casting하면서 소숫점 이하를 떼버린 99에 해당하는 문자를 찍는 것이
당연한 결과고요. -_-a

정리가 됐는지 모르겠습니다.

mastercho의 이미지

위에 제가 마지막으로 글을 썼는데

또다시 답변들을 달으셨네요

충분히 정리가 되었고

이제 쓰레드가 닫혀도 될듯 싶네요

답변 주신 모든 분들에게 감사드립니다 :)

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.