[기초 C 질문] float 타입에 대해서.

MasterQ의 이미지

워낙 float타입은 쓸일이 없어서

생각할 일이 별로 없었는데

다음과 같은 현상이 있더군요..

void foo(){
    ...
    float tmp = -44.22;
    ...
}

gdb에서

(gdb) p tmp
$1 = -44.2200012

(gdb) p/x (unsigned long) tmp
$3 = 0xffffffd4

(gdb) p/x ((unsigned char *) &tmp)[0] @ 4
$11 = {0x48, 0xe1, 0x30, 0xc2}

저는 $3 의 값 (0xffffffd4) 과 $11의 값(0xc230e148)이 왜 다르게 표현되는지 잘 모르겠습니다...

float가 메모리에 어떻게 표현되는지 몰라서 실제로 tmp가 선언된 메모리에 있는 값이 $3인지 $11인지도 모르겠네요..

에공 부끄러워라~ :oops:

seoleda의 이미지

소수부분이 정확이 2의 몇 승으로 표시할수 없기 때문입니다.

십진수를 사용하는 우리들은 소수부분이 2나 5로 나누어 떨어지 않는 다면 순환소수가 나오잖아요..

1+1/3 을 생각해보면 1.3333333.. 이죠? 그럼 이 수를 제한된 자리수로 표시를 하려면 1.33334 이나 1.33333 이정도로 표시해야겠죠?
같은 이유로 컴퓨터는 에서는 소수부가 2로 정확이 나누어 떨어지지 않는다면 역시 순환소수로 표시됩니다. 그런데 float 형은 자릿수에 제한이 있으므로 가장 가까운 근사치로 저장되는 것입니다.

MasterQ의 이미지

답변 감사드립니다.

근데 제가 궁금한것은 왜 같은 숫자가 0xffffffd4 와 0xc230e148로 다르게 표현되는지 입니다.. 다른말로 float형이건 무슨 다른값이건 간에 4바이트 메모리에 있는값이 그대로 나올것으로 예상했는데 두 값이 4바이트를 한꺼번에 읽은것과 한바이트씩 읽은것과 전혀 다르게 나오는 이유를 모르겠습니다.

혹시 여기에 대한 답변이었으면 제가 잘 이해를 못해서 그러는데 추가 설명을 부탁 드려도 될까요?

낙엽의 이미지

제가 알고있기로..(틀릴수도 있습니다. -_-)

float 으로 선언한 변수에 값을 찍어보면,

float i=-44.22;

printf("%2.2f\n",i);
로 지정하면 당연히 -44.22라는 값이 나오게 됩니다.
그런데 그 값이 실제 저장되는 값은 -44.22 라는 값이 아니죠.
실제 그 하위에 근사값이 저장되게 되는데, 그 값이 출력될 때에는
일정크기로 잘라 보여주게 됩니다.

float i=-44.22;

printf("%2.20f\n", i);
이렇게 하위값까지 모두 찍어보세요. 그 밑으로도 일정 수치까지
근사치는 계속진행됩니다.
당연히 소숫점 하위의 값이 보여지는것과 실제값이 차이가 있으니
그 값에 차이가 있다고 생각을 하고 있습니다만..
windy96의 이미지

union으로 확인해보시죠.

#include <stdio.h>

union u_for_float {
	float		f;
	unsigned int		i;
	unsigned char	c[4];
};

int main() {
	union u_for_float	uf;
	uf.f = -44.22;
	printf("float: %f\n", uf.f);
	printf("unsigned int: %d\n", uf.i);
	printf("chars: %x %x %x %x\n", uf.c[0], uf.c[1], uf.c[2], uf.c[3]);
}  

float: -44.220001
unsigned int: 3257983304d
chars: 48 e1 30 c2

라는 결과가 나오네요. x86, cygwin에서의 결과입니다. (endian 감안하시길)

MasterQ의 이미지

windy96 wrote:
union으로 확인해보시죠.

#include <stdio.h>

union u_for_float {
	float		f;
	unsigned int		i;
	unsigned char	c[4];
};

int main() {
	union u_for_float	uf;
	uf.f = -44.22;
	printf("float: %f\n", uf.f);
	printf("unsigned int: %d\n", uf.i);
	printf("chars: %x %x %x %x\n", uf.c[0], uf.c[1], uf.c[2], uf.c[3]);
}  

float: -44.220001
unsigned int: 3257983304d
chars: 48 e1 30 c2

라는 결과가 나오네요. x86, cygwin에서의 결과입니다. (endian 감안하시길)

네 그 결과(48 e1 30 c2)는 gdb로 print한 0xc230e148 ({0x48, 0xe1, 0x30, 0xc2}) 동일합니다. 그리고 올리신 코드에서는 0xc230e148 과 동일한 integer값 3257983304d 이 출력됩니다.

그러나 다음 코드를 한번 실행시켜봐주시기 바랍니다.

#include <stdio.h>

union u_for_float {
   float      f;
   unsigned int      i;
   unsigned char   c[4];
};

int main() {
   union u_for_float   uf;
   float t = -44.22;
   uf.f = -44.22;
   printf("float: %f\n", uf.f);
   printf("unsigned int: %x\n", (unsigned int)uf.i);
   printf("unsigned int: %x\n", (unsigned int)t);
   printf("chars: %x %x %x %x\n", uf.c[0], uf.c[1], uf.c[2], uf.c[3]);
}

결과:

float: -44.220001
unsigned int: c230e148
unsigned int: ffffffd4
chars: 48 e1 30 c2

unsigned int: ffffffd4 ............

왜 이렇게 나오는것일까요???

kslee80의 이미지

일단, -44.22 가 메모리 상에서는 전혀 엉뚱한 값으로 보이는 이유는 간단합니다.
일반 정수형 변수 타입과는 틀리게 float 와 double 의 경우에는 IEEE 에서 메모리 사용 구조 자체를 따로 정의해 놓았습니다.
그리 크게 신경 쓸 필요 없는거라 기억은 잘 안 나지만....
지수 형태로 표기해서,
앞에 몇 비트가 유효자리, 나머지 비트가 자릿수를 의미하는 것으로 기억합니다.

예를 들자면,
65.4 를 지수 형태로 표기하면 6.54e+01 로 표기가 되는데,
유효자리 부분에는 654 를 기록하고, 뒤에 자릿수 필드에 1 을 기입하는것으로
65.4 를 메모리상에 보관하게 됩니다.
컴퓨터 관련 학과를 나왔다면, 아마 들은 기억이 있으실 겁니다.
(컴퓨터 구조와 PL 에서 이 부분을 다루죠.)

(워낙에 오래전에 배운거라 기억이 잘 안 나서 부정확 하네요...-.-;; )

그렇기 때문에 메모리에 있는 값을 찍은것과 실제 변수의 출력 결과가 틀린 것이구요,
맨 마지막에 unsigned int 로 찍은 값은 -44 입니다.
눈치채셨을수도 있겠지만,
float 값을 unsigned int 로 캐스팅 하는 것을 컴파일러가 인지해서,
float -> unsigned int 로 해석(?) 해서 그 값을 사용한 것입니다.
(정확하진 않지만, 의미상으로는 이렇게 이야기 하는게 맞겠죠..)

sharefeel의 이미지

정수로 캐스팅하실 때 실수 하셨습니다.
제가 고쳐보았습니다.
(unsigned int)t ==> *((unsigned int *)&t)
수정한 소스와 결과 같이 올립니다.
두번째부터 네번째까지 같은 값으로 출력된 것을 볼 수 있습니다.

#include <stdio.h>

union u_for_float {
    float      f;
    unsigned int      i;
    unsigned char   c[4];
};

int main() {
    union u_for_float   uf;
    float t = -44.22;
    uf.f = -44.22;
    printf("float: %f\n", uf.f);
    printf("unsigned int: %x\n", (unsigned int)uf.i);
    printf("unsigned int: %x\n", *((unsigned int *)&t));
    printf("chars: %x %x %x %x\n", uf.c[0], uf.c[1], uf.c[2], uf.c[3]);
}

결과
float: -44.220001
unsigned int: c230e148
unsigned int: c230e148
chars: c2 30 e1 48

===============
Vas Rel Por

windy96의 이미지

기본적으로 모든 변수는 signed이며, unsigned와 signed를 섞어쓰면 문제가 발생합니다.
지금 문제도 float가 signed인데 unsigned int로 casting을 하니 FFFF...가 보이는 것이지요. signed int로 casting을 하면 -44가 그냥 나옵니다. 물론 FFFF...는 음수의 표기형태이긴 하나 반가운 모습은 아니지요.

catzbi의 이미지

struct saveMe { 
	union test{
		float a,b,c,d; 
		float abcd[4] ;
	}t;
};

void main () { 

	vector <float > vf ; 
	vf.push_back ( 0.1 ); 
	vf.push_back ( 0.2 );	
	vf.push_back ( 0.3 ); 
	vf.push_back ( 0.4 ); 

	copy ( vf.begin(), vf.end(), ostream_iterator < float > ( cout, " " ) )  ; 
	
	saveMe s; 
	s.t.a= vf[0];
	s.t.b= vf[1];
	s.t.c= vf[2];
	s.t.d= vf[3]; 

	cout << s.t.a << ends << s.t.b << ends
		<<s.t.c << ends << s.t.d << endl; 
	cout << s.t.abcd[0] << ends << s.t.abcd[1] <<ends
		<<s.t.abcd[2] << ends << s.t.abcd[3] << endl; 



}

float (&)[4] != float a,b,c,d 맞나요? [msvc++6]


	float a= 0.1; 
	float b= 0.2 ; 
	float c= 0.3; 
	float d= 0.4 ; 
	float abcd[4]={a,b,c,d}; 
	cout << a << b << c << d; 
	cout << abcd[0] << abcd[1] << abcd[2] << abcd[3]<<endl; 

에러 않나던데요.

ssehoony의 이미지

(gdb) p/x (unsigned long) tmp

이렇게 해서 보면 tmp 가 일단 -44 로 변경되고 그 값을 찍는거기 때문에
-44 에 해당하는 unsigned long 값을 찍힌거니깐 정상적인 경우입니다.

아마 질문하신분은 float 의 메모리에 적힌 값을 보고 싶으셔서 그런 것 같은데 (unsigned long) tmp 으로 캐스팅 되는 순간에 원본 float 에서 다른 값을 변하고 그걸 gdb 가 16진수로 표기를 해서 그렇게 된거져.

결론적으로 $11 이 메모리에 있는거 맞습니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.