2017-03-13

round(1234.5)의 결과는 무엇일까

 혹시 1235라고 생각했는가? 그렇다면 아마 다음과 같은 과정을 거쳤을 것이다. round는 주어진 실수를 정수로 반올림하는 것이다. 반올림은 가장 가까운 수를 만드는 것이다. 반올림할 자릿수가 0, 1, 2, 3, 4이면 내리고 5, 6, 7, 8, 9이면 올린다. 따라서 1234.5의 5는 올려야 하므로 답은 1235이다. 만약 이렇게 생각했다면 round가 무엇인지 제대로 이해하지 못한 것이다.

 round가 무엇인지 알기 위해 우선 round의 정의부터 확실히 해보자. 흔히들 착각하는 것이 ceil은 올림, floor는 내림, round는 반올림이라고 생각하는 것이다. 하지만 round는 반올림이 아니다. Wikipedia에서 말하는 round의 정의는 다음과 같다.

Rounding a numerical value means replacing it by another value that is approximately equal but has a shorter, simpler, or more explicit representation.

 round란 것은 반올림이 아니라 근삿값을 구하는 것이다. 즉, 반올림을 구하는 것도 round이지만, 올림과 내림도 round고, 12345678 같은 복잡한 분수를 15로 간단하게 표현하는 것도 round이다.

 또한, 반드시 일의 자리까지 round 할 필요도 없다. 물론 보통의 round 구현은 실수를 입력받아 정수 근삿값을 만든다. 하지만 반드시 이럴 필요는 없다. 경우에 따라서 십의 자릿수 까지 근사값을 만들 수도 있고, 백의 자릿수 까지 근사값을 만들 수도 있다. 즉, round(1234.5)의 답은 1000이 될 수도 있고, 1200이 될 수도 있고, 50 단위로 근사값을 구한다면 1250이 될 수도 있다.

 따라서 round를 어떻게 구현했는지 사용하는 라이브러리의 스펙을 확인해봐야 한다.

2017-03-05

JavaScript와 IEEE 754

 JavaScript는 표준에서 숫자 타입은 IEEE 754-2008, 64 bit format을 따른다고 명시돼있다. 따라서 숫자 타입의 연산도 IEEE 754-2008을 따를 거로 생각했다. 하지만 ECMAScript 명세에서 NaN이나 Infinity를 포함한 연산에 대해 다른 결과를 내도록 정의한다.

 그 대표적인 경우가 1-1이다. IEEE 754-2008는 지수를 계산하는 방법을 3가지 정의한다. 첫 번째는 pown으로 지수가 정수인 경우에 대해 정의돼있고, 두 번째 pow는 밑과 지수가 모든 실수인 경우 사용할 수 있도록 정의돼 있고, 마지막은 powr으로 밑이 0 이상의 실수인 경우에 대해서 정의돼 있다. 우선 첫 번째인 pown는 지수가 가 되지 못하므로 관심 대상이 아니다. 다른 두 지수 함수인 powpowr1에 대해 다른 결과를 내도록 정의한다. pow1을 리턴하고, powrinvalid operator exception을 발생하도록 정의했다. 사실 이는 양쪽 다 이상한 것은 아니다. limx1x1에 수렴하지만 1 자체는 부정이기 때문에 1인 경우와 invalid operator exception이 발생하는 경우 양쪽 모두 말이 되기 때문이다. 그렇다면 IEEE 754-2008은 -1를 어떻게 정의할까?
 이 경우 밑이 0보다 작으므로 pow만이 유효한 함수고, 이에 대해서 1을 리턴하도록 정의했다. limx-1x는 발산하고, -1는 부정이기 때문에 이는 수학적으로 올바른 정의가 아니고, IEEE 754에서 어떻게 이렇게 정했는지는 모르겠다. 역사적인 이유이거나 이렇게 할 경우 구현이 편해지기 때문일 것인데 혹시 정확한 이유를 알고 있는 사람이 있으면 알려주기 바란다.

 ECMAScript에서는 1-1의 연산에 대해서 NaN을 리턴하도록 정의했다. 이는 ECMAScript는 1997년 만들어졌다. 즉, 아직 IEEE 754-2008이 나오기 전이었고 그 당시는 가 지수인 것에 대해 어떻게 계산해야 하는지 표준이 없었다. 그래서 ±1x일 경우 부정이라고 하는 수학적 성질을 따라 NaN이라고 정하였고, format은 IEEE 754-2008을 쓰기로 한 지금도 NaN을 리턴하도록 정해진 것이다.

 또 한 가지 다른 점은 나머지 연산이다. 나머지는 xy로 나눴을 때, x-q*y로 정의된다. 이는 xy가 둘 다 양수일 경우 명확하지만, 둘 중 하나라도 음수가 되면 q를 정하는 다양한 수가 나올 수 있다. IEEE 754-2008은 이에 대해서 단순한 솔루션을 제시한다. round(x / y)q로 삼는 것이다. 이는 간단하게 구현할 수 있지만, 일반적으로 예상하는 나머지가 나오지 않는 경우가 많아 많은 언어에서 float division은 다른 방식으로 정의하여 사용한다. 이는 ECMAScript도 마찬가지다. ECMAScript는 floor(abs(x / y))를 절댓값으로 가지고, xy의 sign이 같으면 양수, 다르면 음수인 값을 q로 삼는다.