메아리 저널

나루의 자료형 (3)

이전 글에서 이어진다.

실수

나루의 실수형은 두 가지로 나뉜다.

  1. 정확한 실수형. IEEE 854에 따르는 구현체로 유효자릿수를 자유롭게 설정할 수 있으며 숫자에 그 유효자릿수가 계속 유지되어 붙어 다니는 형식이다.
  2. 부정확한 실수형. 이 자료형은 프로세서가 지원하는 기본 자료형을 그대로 사용하며, 일반적으로는 IEEE 754에 따른다.

정확한 실수형을 따로 만든 것은 유리수 자료형과 비슷한 이유에서이다. 또한 정확한 실수형은 부정확한 실수형에 비해 느리긴 해도 정확성을 보장하고 사용자가 오류 처리 방법을 지정할 수 있기 때문에, 정확한 계산이 필요한 회계 처리 등에서 중요하다.

다른 언어에 비해 나루가 유별(?)난 것은 정확한 실수형이 기본이라는 것이다.

...   1.2299   1.2300   1.2301   ...

숫자 뒤에 붙는 여분의 0은 그 자체로 유효자리를 나타내기 때문에 무시되지 않는다. 즉, 86.400 - 0.304 == 86.096이지만 86.4 - 0.304 == 86.1이 된다. (초등학교 쯤에 배웠던 유효자리 계산을 생각해 보시라!)

반면 부정확한 실수형은 뒤에 f를 붙여 표기한다. (지금까지 나왔던 거의 모든 접두·접미사들이 그랬듯이, 이 글자도 소문자만 허용된다. 그러나 과학적 표기법에서 쓰이는 E는 예외이다.)

...   1.2299f   1.23f   1.2301f   ...

어느 경우에나 3.70E+15와 같은 과학적 표기법은 허용된다. 물론 여분의 0을 어떻게 처리하느냐는 뒤에 f가 붙었는가 안 붙었는가에 따라 다르다. 이와 더불어 정수형에서 소개했던 자리 구분자 _도 마찬가지로 쓸 수 있다.

부정확한 실수형의 경우 한 가지 고려사항이 더 있다. 십진법으로 쓴 숫자를 인터프리터 혹은 컴파일러가 어떻게 이진법의 네이티브 실수로 변환하는가? C/C++는 이 변환 과정을 없애기 위해 0xFAC.ADEp+42 형태의 '16진' 실수 리터럴을 지원했다. 과연 이런 게 필요할까?

여기에 대해서는 아직 결정된 게 없다. 한 가지 아이디어라면 16'FAC.ADE'e+42f와 같이 정수에 쓰이는 문법을 그대로 확장하는 것이 있을 수 있겠으나, 이를 부정확한 실수형까지 확대하면 유효자리를 결정하기 매우 힘들어진다는 문제가 있다.

복소수

복소수형이 언어에 별도로 필요한 이유는 여기 저기 많이 떠 돌아 다니지만 대강 정리하면 다음과 같다.

  • 언어에서 지원하는 도메인을 넘어설 수 있는 연산을 기본 지원하는 건 그다지 사리에 맞지 않다. (예를 들어서 음의 실수의 제곱근은 복소수가 되므로, 복소수는 지원할 만한 가치가 있다는 주장)
  • 수학이나 과학 분야에서 복소수는 여기 저기 쓸모가 많다. 특히 전기 공학 같은 분야에서.
  • 언어에서 복소수형을 기본으로 지원하지 않으면 다른 언어와의 인터페이스가 제각기 만들어질 우려가 있다. (실수부가 먼저인가 허수부가 먼저인가 등등)

뭐 그런 이유로 파이썬에는 이미 복소수형이 내장되어 있고, 루비에도 들어 간다는 얘기를 들었던 것 같다. (링크를 걸고 싶은데 검색해 보니 안 나오므로 다음 기회에...) 물론 나루에도 들어 갈 예정이며, 파이썬의 접근 방식을 취할 것이다. 즉,

0j   1j    2j    3.14j   2+3j   0.7e9-15e3j   ...

처럼 뒤에 j를 붙여 허수 리터럴을 만든다. (이는 전기 공학 등에서 쓰이는 표기법이다.) 복소수 리터럴은 별도로 존재하지 않으나, 실수 리터럴과 허수 리터럴을 합쳐서 복소수로 연산되는 상수 식을 만들 수 있다.

나루에서 복소수형을 구현하는 데 있어서 가장 큰 문제는 복소수형이 정확해야 하나 정확하지 않아야 하나이다. 물론 그 쓰임새로 생각해 보면 정확하지 않은 게 더 나을 수도 있지만, 1r3j와 같은 생각 외의 리터럴을 허용해야 할 지 생각해 보면 문제가 복잡해진다. 몇 가지 선택이 있는데,

  1. 정확하지 않은 복소수형만 구현하며 정확한 실수와의 연산을 금지한다. 즉 3.14f * 1j == 3.14j이다.
  2. 1번과 같으나 f를 항상 요구한다. 즉, 3.14f * 1j == 3.14fj이다.
  3. 1번이나 2번과 같으나 정확한 실수나 유리수와의 연산을 허용한다. 즉, 3r8 * 1j == 0.375j이다.
  4. 정확한 복소수형을 함께 구현한다. 즉 3.14f * 1fj == 3.14fj이고, 3.140 * 1j == 3.140j이다.
  5. 복소수형을 일반화시킨다. 즉 1r3j와 같은 값도 허용하며, 그 형식은 C++식으로 말하자면 Complex<Rational>이다.

현재는 1번을 선호하고 있으나 맘이 바뀌면 바뀔 지도 모르니 믿진 마시라.

숫자형 사이의 연산

숫자형에 대한 설명이 모두 끝났으니 다음에는 이들 사이의 연산에 대해서 부가적으로 설명을 덧붙여야 겠다.

전체 글 목록은 다음과 같다.

(2010-04-03)

이 글은 본래 http://mearie.org/journal/2007/08/datatype-in-naru-part-3에 썼던 것을 옮겨 온 것입니다.


(rev 1d46270eb038)