Perl에서의 정수와 실수
Perl에서 정수 및 실수는 다른 언어에서 다루어지는 방식과 거의 비슷하다.
일단 정수에서는 32bit 아키텍쳐의 경우 부호 없는 정수의 최대 표현범위는 0 ~ 4294967295 (0 ~ 2**32-1 )이며 부호 있는 정수의 최대 표현범위는 부호bit 1개(음수일 때 1)가 빠지므로 -2147483648 ~ 2147483647 (-2**31 ~ 2**31-1) 가 된다.
Perl에서 정수의 크기는 64bit 형을 사용하고 싶을 경우 컴파일 시 http://search.cpan.org/dist/perl/INSTALL#64_bit_support의 설명대로 64bit 지원을 하도록 컴파일 하면 된다. 일단 여기서는 32bit 정수를 가정하고 설명한다.
Perl은 내부적으로 정수와 실수를 다음과 같은 자료구조에 저장한다.
자세한 내용은 http://perldoc.perl.org/perlguts.html#Working-with-SVs 참고
-1,1,부호 있는 최대정수값 2147483647에 대해서는 IV
2147483648은 부호 있는 정수형이 표시할 수 있는 영역을 넘어섰으므로 UV
부호 없는 정수의 최대값 4294967295 도 UV
그것을 넘어선 4294967296는 정수형이 표시할 수 있는 범위를 넘어섰으므로 실수형으로 변환되어 NV 로 나온다.
그럼 실수형은 어떠할까?
Perl은 내부적으로 실수를 저장할 때 double(64bit)형을 사용한다. 이것을 다루는 방법은 해당 운영체제의 C library가 double형을 다루는 방법을 그대로 사용한다.
따라서 아키텍쳐 마다 다를 수 있지만 대부분의 시스템의 경우 IEEE 754 floating-point standard를 따르고 있으므로
그 동작방식을 보면 이해할 수 있다.
double형에 대한 표준은 http://en.wikipedia.org/wiki/IEEE_floating-point_standard#Double-precision_64_bit 에 있는 바와 같이 sign, exponent, fraction(mantissa) 비트들로 나뉘어 있다.
실수형은 fraction(mantissa)에 sign(부호)와 exponent(지수)를 곱해서 나타나기 때문에
exponent에 의해 표현할 수 있는 숫자의 범위는 넓어서 double의 경우 2의 -1024~1024승 10의 승수로 변환하면 -308~308승 까지 되지만 fraction(mantissa)의 비트 수가 52bit로 제한 되어 있기때문에 정확하게 나타낼 수 있는 유효숫자 자리수의 범위는 15~16자리수로 한정된다. 수치해석을 공부해본 사람이면 이런 부동소숫점의 특성으로 인해 똑같은 연산이라도 연산의 순서와 방법에 따라 다른 유효성의 상실이 일어나고 오차를 줄이기 위해 이것을 회피하는 방법에 대해서 배운적이 있을 것이다.
이것도 그 한계를 넘나들며 확인해보자.
결과를 보면 fraction(mantissa)가 나타낼 수 있는 정확한 자리수 내에서는 정확하게 표현되지만 그 한계를 넘은 2.000000000000001 의 경우 0.000000000000001 이 round-off되어 2로 된다.
반면 0.000000000000001은 지수형태로 제대로 표시되었다. 왜 2.000000000000001의 0.000000000000001과 그냥 0.000000000000001이 다르게 다루어지는지는 fraction(mantissa)를 이해한다면 알 수 있을 것이다.
그런데 만약 Perl에서 위의 한계들을 뛰어넘는 수들을 다루려면 어떻게 하면 될까?
그건 이미 Perl CORE모듈로 들어가 있는 다음의 모듈들을 사용하면 된다.
일단 정수에서는 32bit 아키텍쳐의 경우 부호 없는 정수의 최대 표현범위는 0 ~ 4294967295 (0 ~ 2**32-1 )이며 부호 있는 정수의 최대 표현범위는 부호bit 1개(음수일 때 1)가 빠지므로 -2147483648 ~ 2147483647 (-2**31 ~ 2**31-1) 가 된다.
Perl에서 정수의 크기는 64bit 형을 사용하고 싶을 경우 컴파일 시 http://search.cpan.org/dist/perl/INSTALL#64_bit_support의 설명대로 64bit 지원을 하도록 컴파일 하면 된다. 일단 여기서는 32bit 정수를 가정하고 설명한다.
Perl은 내부적으로 정수와 실수를 다음과 같은 자료구조에 저장한다.
자세한 내용은 http://perldoc.perl.org/perlguts.html#Working-with-SVs 참고
Perl 내부자료구조를 보여주는 Devel::Peek 모듈을 사용하여 그 경계를 넘나들면서 확인해보자.
IV signed integer
UV unsinged integer
NV double
PV string
SV scalar
#!/usr/bin/perl
use strict;
use warnings;
use Devel::Peek;
Dump(1);
Dump(-1);
Dump(2147483647);
Dump(2147483648);
Dump(4294967295);
Dump(4294967296);
<결과>
SV = IV(0xa8be5c) at 0x3d5fc0
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,IOK,READONLY,pIOK)
IV = 1
SV = IV(0xa8be64) at 0x3d5f9c
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,IOK,READONLY,pIOK)
IV = -1
SV = IV(0xa8be60) at 0x3d5fa8
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,IOK,READONLY,pIOK)
IV = 2147483647
SV = IV(0xa8be68) at 0x3d5fd8
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,IOK,READONLY,pIOK,IsUV)
UV = 2147483648
SV = IV(0xa8be6c) at 0x3d5fe4
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,IOK,READONLY,pIOK,IsUV)
UV = 4294967295
SV = NV(0xaa2a54) at 0x3d5f78
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,NOK,READONLY,pNOK)
NV = 4294967296
-1,1,부호 있는 최대정수값 2147483647에 대해서는 IV
2147483648은 부호 있는 정수형이 표시할 수 있는 영역을 넘어섰으므로 UV
부호 없는 정수의 최대값 4294967295 도 UV
그것을 넘어선 4294967296는 정수형이 표시할 수 있는 범위를 넘어섰으므로 실수형으로 변환되어 NV 로 나온다.
그럼 실수형은 어떠할까?
Perl은 내부적으로 실수를 저장할 때 double(64bit)형을 사용한다. 이것을 다루는 방법은 해당 운영체제의 C library가 double형을 다루는 방법을 그대로 사용한다.
따라서 아키텍쳐 마다 다를 수 있지만 대부분의 시스템의 경우 IEEE 754 floating-point standard를 따르고 있으므로
그 동작방식을 보면 이해할 수 있다.
double형에 대한 표준은 http://en.wikipedia.org/wiki/IEEE_floating-point_standard#Double-precision_64_bit 에 있는 바와 같이 sign, exponent, fraction(mantissa) 비트들로 나뉘어 있다.
실수형은 fraction(mantissa)에 sign(부호)와 exponent(지수)를 곱해서 나타나기 때문에
exponent에 의해 표현할 수 있는 숫자의 범위는 넓어서 double의 경우 2의 -1024~1024승 10의 승수로 변환하면 -308~308승 까지 되지만 fraction(mantissa)의 비트 수가 52bit로 제한 되어 있기때문에 정확하게 나타낼 수 있는 유효숫자 자리수의 범위는 15~16자리수로 한정된다. 수치해석을 공부해본 사람이면 이런 부동소숫점의 특성으로 인해 똑같은 연산이라도 연산의 순서와 방법에 따라 다른 유효성의 상실이 일어나고 오차를 줄이기 위해 이것을 회피하는 방법에 대해서 배운적이 있을 것이다.
이것도 그 한계를 넘나들며 확인해보자.
#!/usr/bin/perl
use strict;
use warnings;
use Devel::Peek;
Dump(2.0);
Dump(2.000000000001);
Dump(2.000000000000001);
Dump(0.000000000000001);
<결과>
SV = NV(0xaa2a44) at 0x3d5fc0
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,NOK,READONLY,pNOK)
NV = 2
SV = NV(0xaa2a54) at 0x3d5f90
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,NOK,READONLY,pNOK)
NV = 2.000000000001
SV = NV(0xaa2a5c) at 0x3d5fa8
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,NOK,READONLY,pNOK)
NV = 2
SV = NV(0xaa2a64) at 0x3d5fd8
REFCNT = 1
FLAGS = (PADBUSY,PADTMP,NOK,READONLY,pNOK)
NV = 1e-015
결과를 보면 fraction(mantissa)가 나타낼 수 있는 정확한 자리수 내에서는 정확하게 표현되지만 그 한계를 넘은 2.000000000000001 의 경우 0.000000000000001 이 round-off되어 2로 된다.
반면 0.000000000000001은 지수형태로 제대로 표시되었다. 왜 2.000000000000001의 0.000000000000001과 그냥 0.000000000000001이 다르게 다루어지는지는 fraction(mantissa)를 이해한다면 알 수 있을 것이다.
그런데 만약 Perl에서 위의 한계들을 뛰어넘는 수들을 다루려면 어떻게 하면 될까?
그건 이미 Perl CORE모듈로 들어가 있는 다음의 모듈들을 사용하면 된다.
- Math::BigFloat - Arbitrary size floating point math package
- Math::BigInt - Arbitrary size integer/float math package
0 TrackBacks
Listed below are links to blogs that reference this entry: Perl에서의 정수와 실수.
TrackBack URL for this entry: http://aero.sarang.net/cgi-bin/mt/mt-tb.cgi/73
Perl 관련해서 부탁할 게 있습니다. 이메일 좀 부탁드려요. 메일 주소가 어디에도 없네요. ^^;