차이점

이 페이지의 선택한 이전 버전과 현재 버전 사이의 차이점을 보여줍니다.

차이 보기로 연결

ieee_754 [2012-09-23 04:26] (현재)
lifthrasiir 새로 만듦
줄 1: 줄 1:
 +====== IEEE 754 =======
  
 +[[부동소숫점]] [[실수]]를 표현하는 방법과 적용할 수 있는 연산들을 정의하는 [[IEEE표준]]. 최신판은 [[2008년]]에 나온 IEEE 754-2008(= [[ISO IEC IEEE]] 60559:2011)이지만, 그 이전판인 IEEE 754-1985와 비교해서 달라진 점은 없고 몇 가지 포맷 및 연산이 새로 추가되었을 뿐이다. 또한 기존에는 [[이진법]] 부동소숫점을 정의하는 IEEE 754(-1985)와 [[십진법]] 부동소숫점을 정의하는 IEEE 854-1987이 공존했으나, 2008년 표준은 이진법과 십진법을 함께 포함하고 있다.
 +
 +명실상부하게 [[유니코드]]와 더불어 [[프로그래머]]가 반드시 알아야 할 표준 중 하나이다. 다른 말로 하면, IEEE 754는 실수 표현이 필요한 거의 모든 곳에서 공통적으로 쓰이고 있으며, 아무 지식 없이 직관적으로 쓰려다 피를 보는 경우도 엄청나게 많다는 얘기다. 이 쪽 관련해서는 [[부동소숫점]] 문서를 참고하는 게 더 나을 것이다. 시간이 나면 맨 아래에 있는 바깥 링크에 있는 문서들도 읽어 보자.
 +
 +===== 포맷 =====
 +
 +IEEE 754에서 정의하는 부동소숫점 실수 포맷은 크게 다음과 같은 변수로 일반화할 수 있다:
 +
 +  * 밑수 //b//
 +  * 지수부의 범위 //e//<sub>min</sub>, //e//<sub>max</sub>
 +  * 가수부(mantissa)를 //b//진법으로 표현했을 때 표현되는 자릿수 //n//
 +  * 가수부의 [[#정규화]] 강제 여부를 나타내는 숫자 //R// (0 또는 1)
 +
 +이제 모든 부동소숫점 소수는 부호 //s//, 지수부 //e//, 가수부 //m//의 [[순서쌍]] (//s//, //e//, //m//)으로 표현할 수 있으며, 그 의미는 다음과 같이 결정된다.
 +
 +^  //s//  ^  //e//  ^  //m//  ^  실수값  ^
 +|  0  |  0  |  0  | 0 |
 +|  1  |  0  |  0  | [[마이너스0|-0]] |
 +|  0/1  |  0  |  (0, 1)  | (-1)<sup>//s//</sup> × //b//<sup>//e//<sub>min</sub></sup> × //m// |
 +|  0/1  |  [1, //e//<sub>max</sub> - //e//<sub>min</sub>]  |  [0, 1)  | (-1)<sup>//s//</sup> × //b//<sup>//e// + //e//<sub>min</sub></sup> × (//R// + //m//) ÷ (//R// + 1)((즉, //m//은 정규화를 할 때(//R//=1)는 [½, 1)의 범위로 변환되고, 정규화를 하지 않을 때(//R//=0)는 그대로, [0, 1)의 범위로 사용한다.)) |
 +|  0/1  |  //e//<sub>max</sub> - //e//<sub>min</sub> + 1  |  0  | (-1)<sup>//s//</sup> × [[무한대|∞]]; 다만, 반올림의 목적으로는 (-1)<sup>//s//</sup> × //b//<sup>//e//<sub>max</sub></sup> |
 +|  0/1  |  //e//<sub>max</sub> - //e//<sub>min</sub> + 1  |  [0, ½) | 조용한(quiet) [[NaN]] |
 +|  0/1  |  //e//<sub>max</sub> - //e//<sub>min</sub> + 1  |  [½, 1)  | 시끄러운(signaling) NaN |
 +
 +눈치채기 어렵지 않지만 모든 실수는 (-1)<sup>//s//</sup> × //b//<sup>//e'//</sup> × //f//의 형태(거의 대부분의 경우 ½ ≤ //f// < 1)로 저장되며, 좀 복잡해 보이는 위의 규칙들은 지수부 //e'//와 가수부 //f//를 어떻게 최적으로 부호화하느냐에 대한 결과인 것이다. (다만, 편의상 아래에서는 실제 저장되는 가수부 //m// 대신 //f//를 직접 사용하는 경우가 많다.)
 +
 +구체적인 예를 들어 보자. //b// = 2, //R// = 1, //e//<sub>min</sub> = -14, //e//<sub>max</sub> = 15, //n// = 10인 부동소숫점 표현을 생각하면, 0.1은 다음과 같이 표현할 수 있으며:
 +<.center>$$(-1)^0 \times 2^0 \times 0.0001\;1001\;1001\;1001\;1001\cdots_{(2)}$$</>
 +이는 다음과 같으므로:
 +<.center>$$(-1)^{\underline{0}} \times 2^{\underline{11}+(-14)} \times 0.1\;\underline{1001\;1001\;10}01\;1001\cdots_{(2)}$$</>
 +여기서 가수부(//f//)의 첫 1비트는 정규화로 인해 암시적으로 표현된다. 따라서 결과적으로는 부호 //s// = 0, 지수부 //e// = 11 = 01011<sub>(2)</sub>, 가수부 //m// = 0.1001100110<sub>(2)</sub> = 0.599609375으로 표현되며, 대응되는 비트 표현은 ''0 01011 1001100110''가 된다. 이는 실제로 [[binary16]] 포맷에서 0.1을 표현하는 방법이다.
 +
 +==== 정규화 ====
 +
 +비단 IEEE 754 뿐만 아니라 부동소숫점 표현의 중요한 특징 중 하나는 **정규화**(normalization)로, 연산이 끝난 결과를 가수부가 특정 범위 안으로 들어 오도록 지수부를 조정하는 과정이다. 예를 들어서 이진법을 가정할 때, 0.3 = 0.6 × 2<sup>-1</sup>과 0.8 = 0.8 × 2<sup>0</sup>의 합을 계산하는 과정은 다음과 같이 이루어진다.
 +
 +  - 두 숫자를 더 낮은 지수부에 맞게 조정한다: (0.6 × 2<sup>-1</sup>) + (1.6 × 2<sup>-1</sup>)
 +  - 가수부를 더한다: 2.2 × 2<sup>-1</sup>
 +  - 가수부가 범위 안에 들어 오도록 지수부를 바꾼다: 0.55 × 2<sup>1</sup>
 +
 +물론 실제 하드웨어 구현에서는 지수부 차이가 너무 많이 날 경우 가수부를 볼 필요가 없게 하는 등의 최적화가 들어가 있다. 부동소숫점 연산은 그래서 일반적인 정수 연산과는 달리 수행 시간을 전혀 종잡을 수 없는 경우가 대부분이다.
 +
 +정규화와 함께 수반되는 것은 **반올림 모드**(rounding mode)로, 정규화 뒤에 가수부의 일부 끝자리가 잘려나갈 경우 어떻게 처리할 것인지를 나타낸다. 이 "처리"라 함은 단순히 자리를 자르거나 올리는 거 말고도, 결과가 원래 이상적인(가수부가 무한히 긴) 결과와 다를 때 오류를 내는지의 여부라거나, 애초에 이상적인 결과가 불가능할 때의 동작이라거나, 결과가 유한한 부동소숫점 값으로 나타낼 수 없을 때의 동작 따위를 모두 포함한다. IEEE 754-2008에서는 다섯 종류의 반올림 모드를 지원한다(4.3장):
 +
 +  ? 짝수로 반올림 (round to nearest, ties to even)
 +  ! 가장 가까운 표현 가능한 숫자로 반올림하되, 원래 값이 가능한 숫자 두 개의 중간에 걸쳐 있으면 둘 중 가수부의 마지막 자리가 짝수인 값으로 반올림한다. 이 방법은 "은행원의 반올림"이라고도 불리며, 반올림으로 인한 오류의 [[기대값]]을 0으로 하는 효과가 있다.
 +  ? 큰 절대값으로 반올림 (round to nearest, ties away from zero)
 +  ! 앞과 같은데, 중간에 걸쳐 있을 경우 두 값 중 절대값이 큰 값으로 반올림한다.
 +  ? 올림 (round toward +∞)
 +  ! 원래 값보다 크거나 같은 가장 작은 표현 가능한 숫자를 선택한다.
 +  ? 버림 (round toward -∞)
 +  ! 원래 값보다 작거나 같은 가장 큰 표현 가능한 숫자를 선택한다.
 +  ? 절삭 (round toward zero)
 +  ! 원래 값보다 절대값이 작거나 같은, 절대값이 가장 큰 표현 가능한 숫자를 선택한다.
 +
 +기본값은 "짝수로 반올림"이다. 모든 연산은 연산 후 저장 직전까지는 무한히 정확한 값으로 표현된다고 가정하는데, [[단일곱셈누산]](fused multiply-add, FMA) 같이 여러 연산이 하나로 합쳐져 있는 경우에도 마찬가지로 모든 연산이 끝난 다음에야 반올림이 처리된다. (애초에 FMA가 곱셈 후 덧셈을 할 때 정밀도를 향상시키기 위해 존재하는 연산이다.)
 +
 +==== 준정규화 ====
 +
 +IEEE 754가 기존의 부동소숫점 구현들과 가장 크게 차이나는 부분은, 절대값이 매우 작은 숫자를 일반적인 정규 표현에 맞지 않는 표현으로 나타내도록 하는 **준정규화**(subnormalization)이다. IEEE 754-2008 이전에는 비정규화(denormalization)라고도 했는데, 엄밀히 말하면 정규화를 하기는 하는데 덜 정밀하게 표현하는 것에 불과하므로 정확한 용어라고는 할 수 없다.
 +
 +보통과 같이 모든 숫자가 정규화된 체계에서는 두 가지 귀찮은 점이 존재한다. 한 가지는 가수부가 [½, 1)의 범위로 제한되어 있어서 0을 예외 없이 나타내기 어렵다는 것이며, 다른 한 가지는 서로 다른 유한한 실수의 차이가 0이 되는 경우가 존재할 수 있다는 점이다. (고로 a = b와 a - b = 0이 동치가 되지 않는다.) 후자는 특히 [[0으로나누기]]를 유발할 가능성이 있어서 문제가 되었는데, 예를 들어서 나타낼 수 있는 가장 작은 양의 실수가 2<sup>-1022</sup> × 0.5인 경우를 가정하자. 이제 2<sup>-1022</sup> × 0.6과 2<sup>-1022</sup> × 0.7의 차이는 정규화하면 2<sup>-1022</sup> × 0.1 = 2<sup>-1025</sup> × 0.8이지만, 이는 표현할 수 없으므로 반올림을 쓸 경우 0으로 절삭되어 버린다!
 +
 +준정규화는 이런 문제를 해결하기 위해 특정 절대값 이하로는 지수부를 고정시킨다. 따라서 이렇게 표현된 준정규화된 숫자(subnormal number)는 가수부가 [½, 1)이 아닌 [0, 1)의 범위가 되며, 0은 준정규화된 가장 작은 숫자로 표현 가능하다. 그리고 준정규화된 숫자들 사이의 숫자 간격([[ulp]])은 일정하기 때문에, 모든 유한한 실수의 숫자 간격 또한 준정규화된 양의 숫자로 표현할 수 있게 된다. 앞의 예제를 끝마치자면, 두 작은 숫자의 차이 2<sup>-1022</sup> × 0.1은 준정규화되어 지수부가 안 바뀔테니 0으로 절삭되지 않을 것이다.
 +
 +표준화 과정에서는 "점진적인 [[언더플로]]"(gradual underflow)라고 불리기도 했으며, 채택된 제안들 중에서 논란이 가장 크게 일었던 기능이기도 하다. 그도 그럴 것이 준정규화를 구현해서 준정규화된 숫자에 대해서만 느려지면 상관이 없는데, 정규화된 숫자의 경우에도 느려지지 않게 할 방법이 잘 보이지 않았던 것이다. 거기에 [[DEC]]가 [[VAX]]에서 쓰던 자기 포맷을 관철시키려고 준정규화를 반대하는 상황까지 겹쳐서 결정은 쉽지 않은 일이었다. 하지만 정규화된 숫자의 연산에 영향을 주지 않게 하는 기법이 속속 발견되고, 여러 전문가들(여기에는 [[Donald Knuth]]도 있었다)이 찬성표를 던졌으며, 심지어 __DEC가 준정규화 반대를 위해 검토를 요청한 전문가조차 찬성표를 던지자 <del>[[팀킬]]</del>__ 상황은 크게 반전되어 버렸다. 한 술 더 떠서 [[인텔8087]]은 IEEE 754 표준화 도중에 출시되었으며 준정규화를 지원했는데, 이는 IEEE 754 표준이 검증되지 않은 설계에 불과하다는 주장을 깔끔하게 정리하는 실용적인 구현체였다.((이 문단에 관련해서는 IEEE 754 표준의 초안(Kahan-Coonen-Stone 제안)을 쓴 [[William Kahan]]의 1998년 [[http://www.eecs.berkeley.edu/~wkahan/ieee754status/754story.html|인터뷰]]를 참고.))
 +
 +==== 형언하기 어려운 숫자가 아닌 무언가 ====
 +
 +IEEE 754 표준에는 유한한 숫자 말고도 몇 가지 특수한 값들이 존재한다. 이들은 연산들의 결과가 실수가 아니라거나, 실수이긴 한데 표현 가능하지 않은 경우에 등장하며, 결과를 일일이 확인하지 않아도 일단은 연산을 거친 뒤에 뭔가 이해할 수 있는 결과가 나오도록 정의되어 있다.
 +
 +첫번째는 **[[무한대]]**(+∞/-∞)로, 이상적인 계산 결과의 절대값이 표현 가능한 결과를 벗어나는 경우에 등장한다. 예를 들어서 반올림 모드가 "올림"인데 원래 결과가 표현 가능한 가장 큰 유한한 실수보다 크면 그 결과는 +∞가 된다. 또한 반올림 모드가 "짝수로 반올림"이거나 "큰 절대값으로 반올림"일 경우에도 무한대가 없었을 때 무한대에 대응되는 비트 표현의 실수값을 기준으로 반올림이 일어난다. 예를 들어 //b// = 2, //e//<sub>max</sub> = 1023인 경우를 생각하면, 표현 가능한 가장 큰 유한한 실수는 2<sup>1023</sup>보다 살짝 작은데 이 때 +∞를 2<sup>1023</sup>처럼 생각하고 반올림을 수행한다는 얘기가 된다. <del>무한대와 유한한 숫자의 차이가 무한대라고 반올림이 안 되는 건 아니다.</del>
 +
 +두번째는 **조용한 [[NaN]]**(quiet NaN, qNaN)으로, NaN은 "not a number", 즉 숫자가 아님<del>안숫자</del>을 나타낸다. qNaN은 예를 들어서 [[음수]]의 [[제곱근]]같이 실수가 아닌 결과나, ±∞ × 0 같이 결과가 정의되어 있지 않은 경우에 발생한다. qNaN은 기본적으로는 별도의 오류를 발생시키지 않으며, 따라서 qNaN + 3 같이 연산의 피연산자가 qNaN일 때의 결과는 항상 qNaN이다. (다만 일부 연산은 NaN 자체를 입력으로 받지 않는 경우가 있는데, 이 경우에는 오류가 난다.) 또한 중요한 특징으로 NaN이 임의의 추가 정보를 가수부에 들고 있을 수 있다는 점이 있는데(이 추가 정보를 payload라고 부른다), 그래서 피연산자가 qNaN이라서 결과가 qNaN인 경우 **추가 정보가 보존되어야 한다**! (다만 피연산자가 둘 이상 qNaN일 경우 둘 중 뭘 선택하느냐는 구현체 마음.) 따라서 연산 도중에 오류로 NaN이 발생했을 때 당장 확인하지 않아도 나중에 결과가 NaN일 때 추가 정보를 확인하면 한 가지 오류는 확인할 수 있다.((이 특성을 악용(...)하는 대표적인 예로 [[LuaJIT]]이 있다. LuaJIT은 [[binary64]] 포맷의 가수부에 웬만한 [[포인터]] 값이 들어간다는 점에 착안해서 qNaN에 숫자가 아닌 다른 값을 넣는 기행을 저지른다. 이러면 숫자가 아닌 다른 정보를 나타내기 위해 별도의 비트를 둘 필요가 없이 64비트로 모든 값을 나타낼 수 있게 된다.)) 이렇게 쓰고 보니 무한대랑 비슷해 보이는데, 사실은 무한대 자체가 가수부가 0인 qNaN와 동일한 표현을 사용하며 연산에서의 결과만 살짝 다를 뿐이다.
 +
 +세번째는 **시끄러운 NaN**(signaling NaN, sNaN)이다. qNaN과는 달리 sNaN은 피연산자로 들어가는 순간 바로 오류를 발생시킨다. 표준 연산들에서는 반환값으로 sNaN을 주는 경우가 없기 때문에, qNaN은 당장 확인하지 않아도 좋은 오류를 나타내는데, 그리고 sNaN은 당장 확인해야 하는 오류를 나타내는데 쓰는 게 편리하다.
 +
 +엄밀하게 말하면 유한한 숫자이긴 하지만, [[마이너스0|-0]]도 특수한 숫자이기는 매한가지이다. 이 값은 부동소숫점 표현은 어쩔 수 없이 [[부호와절대값표현]]을 써야 하기 때문에 나타나는 값이며, 거의 대부분의 경우에 +0과 동일하게 동작하지만 일부 연산에서 다르게 동작하며(예를 들어 3÷(-0) = -∞) 특정한 최적화가 -0 하나 때문에 안 되는 경우도 생긴다(예를 들어 -(x - y) ≠ y - x). 다만 IEEE 754에서 0이 거의 [[무한소]] 취급받고 있다는 걸 생각하면 무한대가 두 짝이니 무한소가 두 짝 있다고 생각하는 것도 나쁘지는 않...을 듯?
 +
 +==== 목록 ====
 +
 +다음은 IEEE 754-2008이 정의하는 모든 포맷과 대응되는 파라미터의 목록이다.
 +
 +^  이름  ^  비트수  ^  //b//  ^  [//e//<sub>min</sub>, //e//<sub>max</sub>]  ^  //n//  ^  //R//  ^  자릿수(($$(n+R) \log_{10} b$$. 십진법으로 나타냈을 때 유효 자릿수.))  ^  절대값 상한(($$\approx b^{e_{\rm max}}$$. 표현할 수 있는 가장 큰 유한한 실수의 **상한**. 따라서 여기 나온 숫자 자체는 해당 포맷으로 절대로 표현 못 한다.))  ^
 +| **[[binary16]]** (반정도)    |   16 (1+5+10)      |   2 |     [-14, +15]     |   10 |  1 |   3.31 |  3.28×10<sup>4</sup> |
 +| **[[binary32]]** (단정도)    |   32 (1+8+23)      |   2 |    [-126, +127]    |   23 |  1 |   7.22 |  1.71×10<sup>38</sup> |
 +| **[[binary64]]** (배정도)    |   64 (1+11+52)     |   2 |   [-1022, +1023]   |   52 |  1 |  15.95 |  8.99×10<sup>307</sup> |
 +| **[[binary128]]** (사배정도) |  128 (1+15+112)    |   2 |  [-16382, +16383]  |  112 |  1 |  34.01 |  5.95×10<sup>4931</sup> |
 +| **[[decimal32]]**            |   32 (1+7+23+X)    |  10 |     [-95, +96]     |    7 |  0 |   7.00 |  1.00×10<sup>96</sup> |
 +| **[[decimal64]]**            |   64 (1+9+53+X)    |  10 |    [-383, +384]    |   16 |  0 |  16.00 |  1.00×10<sup>384</sup> |
 +| **[[decimal128]]**           |  128 (1+13+113+X)  |  10 |   [-6143, +6144]   |   34 |  0 |  34.00 |  1.00×10<sup>6144</sup> |
 +
 +binary16과 십진법 포맷은 IEEE 754-2008에서 추가되었으며, 괄호 안에 있는 이름은 IEEE 754-1985에서의 명칭이다(다만 "반정도"는 통칭). binary16과 decimal32는 너무 작기 때문에 계산용으로 쓰이진 않고 저장용으로만 권장되고 있다. 비트 수에서 "X"로 표기된 것은 비트 패턴에 따라 지수부와 가수부에 함께 포함되는 비트를 나타낸다.
 +
 +IEEE 754는 또한 앞의 포맷들을 확장하는 확장된(extended)/확장 가능한(extendable) 포맷을 정의하고 있다. 둘의 차이는 순전히 사용자가 //n//과 //e//<sub>min</sub>, //e//<sub>max</sub>를 맘대로 지정할 수 있느냐 없느냐의 차이로, 표준은 앞의 세 파라미터에 한하여 기반 포맷보다 같거나 큰 값을 쓰는 전제 하에 확장을 허용한다. 실질적으로는, 다음 두 포맷이 현실적으로 가장 많이 쓰이는 확장 포맷이다:
 +
 +^  이름  ^  비트수  ^  //b//  ^  [//e//<sub>min</sub>, //e//<sub>max</sub>]  ^  //n//  ^  //R//  ^  자릿수  ^  절대값 상한  ^
 +| **[[x86확장배정도]]**  |  80 (1+15+64)    |  2 |  [-16382, +16383]  |   63 |  0 |  18.97 |  5.95×10<sup>4931</sup> |
 +| **[[double-double]]**  |  128 (1+11+105)  |  2 |   [-1022, +1023]   |  105 |  1 |  31.91 |  8.99×10<sup>307</sup> |
 +
 +===== 연산 =====
 +
 +흔히들 생각하는 것과 다르게 IEEE 754는 포맷 말고도 해당 포맷에 적용할 수 있는 다양한 연산들의 결과를 정의하고 있다. 여기에는 전혀 듣도 보도 못한 것이 포함되기도 하는데, 이 중에는 NaN의 처리 때문에 복잡해지는 것들도 있고:
 +
 +  * 두 수의 최대값/최소값 연산은 둘 중 하나가 NaN이고 나머지 하나가 보통 숫자일 때 잘 정의되지 않는다. IEEE 754는 명시적으로 이 경우에 숫자를 반환하는 ''minNum''/''maxNum'' 계열의 함수를 정의한다. (NaN을 반환하는 쪽은 IEEE 754에는 없지만 일부 프로그래밍 언어가 제공하기도 한다.)
 +  * NaN의 처리에 따라 두 숫자는 한 쪽이 작거나, 크거나, 같거나, 비교가 불가능할 수 있다. 그래서 이걸 토대로 비교 연산을 만들면 6개가 아니라 **22개**라는 말도 안 되는 갯수가 나온다. [[D언어]]가 이 중 일부, 정확히는 14개를 구현하고 있는데, 이는 연산자에 "비교가 불가능한" 경우를 명시적으로 표시하지 않도록 부단한(...) 노력을 기울였기 때문이다.
 +  * IEEE 754는 NaN 때문에 [[완전순서]]가 되지 않는데, 이를 해결하기 위해 NaN에 임의의 순서를 매기는 ''totalOrder'' 함수가 정의되어 있다. 물론 이 함수 한정으로 -0 < +0이다.
 +
 +자주 쓰이는 비표준 수학 함수가 명시적으로 정의되거나, 미묘하게 잘 정의되지 않은 수학 함수가 재정의되기도 하며:
 +
 +  * [[지수함수]]와 [[로그함수]]는 밑수가 [[자연상수]], 2, 10인 경우에 대해서 각각 두 개씩 정의되어 총 12개 있다. 두 개씩 정의된 이유는, 보통의 $$e^x$$와 $$\log x$$ 말고도 $$e^x - 1$$과 $$\log (1+x)$$를 따로 제공하는 게 구현상으로 더 정밀도가 높은 경우가 많기 때문이다. 이 두 변종은 각각 ''expm1''과 ''log1p''라고 명명되었다.
 +  * IEEE 754-1985의 [[거듭제곱]](''pow'' 함수)은 [[0의0제곱]]을 1로 정의한다. 해당 문서를 보면 알겠지만 이는 아주 말이 안 되는 정의는 아니지만, 이렇게 동작하지 않는 걸 선호하는 사람들도 있기 때문에 IEEE 754-2008에서는 ''pown''(지수가 정수여야 하며, //x//<sup>0</sup> = 1로 정의) 함수와 ''powr''(밑수가 항상 양수여야 함) 함수가 새로 추가되었다.
 +  * 모든 삼각함수에는 [[호도법]]으로 표현될 인자/결과값에 [[원주율]]을 곱한 버전이 하나씩 더 있다. 예를 들어 ''sinPi(0.5) = sin(pi/2)''.
 +
 +그냥 그 의미를 알 수 없는 것조차 있다:
 +
 +  * ''copysign(x,y)'' 함수는 x에서 부호를, y에서 절대값을 따 온 새로운 실수를 반환한다. 이 함수의 거의 유일한 쓸모는 -0과 +0을 구분하는 용도. 뭐시라?!
 +  * [[나머지연산]]은 피연산자 중 하나라도 음수일 때 잘 정의되지 않는데, IEEE 754 ''remainder'' 함수는 이 중 한 가지 경우를 엄밀하게 정의한다. 그런데 이 한 가지 경우라는 게... //x//를 //y//로 나눈 나머지를 //z//라 할 때, //z//가 ±½//y// 사이에 들어오도록 //z//를 정의하는 것인지라 **//x//와 //y//가 양수여도 음수 나머지가 나올 수 있다.** 예를 들어 ''remainder(8, 3) = -1''. <del>[[무슨마약하시길래이런생각을했어요]]</del>
 +
 +===== 오류 =====
 +
 +연산들은 각종 오류(7장)를 발생시킬 수 있는데, 총 다섯 개가 존재한다. 기본적으로는 이들 오류가 났을 때 연산이 중지되지는 않고 특정한 플래그만을 설정시킨 뒤 기본 반환값을 반환하며 속행되지만 사용자가 오류시 행동을 지정하는 것도 가능하다(8장). 오류의 종류는 다음과 같다.
 +
 +  ? 잘못된 연산 (invalid operation)
 +  ! 연산의 결과가 정의되지 않거나 인자가 예상한 범위를 벗어나는 경우. 연산이 qNaN을 반환하는 경우가 사실은 바로 이 오류가 (조용히) 처리되었을 경우 나오는 것이다.
 +  ? 0으로 나누기 (division by zero)
 +  ! 말 그대로 나눗셈의 오른쪽 피연산자가 +0 또는 -0인 경우. 기본 동작은 +∞ 또는 -∞를 반환하는 것이다.
 +  ? 오버플로 (overflow)
 +  ! 반올림 처리를 했는데 원래 값이 ±∞이 아닌데도 그 결과가 +∞ 또는 -∞이 되어야만 하는 경우. 기본 동작은 부정확 플래그를 함께 설정하는 것이다(당연히, 오버플로가 되었으면 부정확한 것이니까).
 +  ? 언더플로 (underflow)
 +  ! 반올림 처리를 했는데 원래 값이 ±0이 아닌데도 그 결과가 +0 또는 -0이 되어야만 하는 경우. 기본 동작은 오버플로와 비슷하다.
 +  ? 부정확 (inexact)
 +  ! 연산의 결과가 무한히 정확한 가수부와 범위가 무제한인 지수부로 표현했을 때의 결과랑 다를 경우.
 +
 +===== 같이 보기 =====
 +
 +  * [[펜티엄FDIV버그]]
 +
 +===== 바깥 링크 =====
 +
 +  * 〈[[http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html|모든 전산학자가 부동소숫점 산술에 대해 알아야 할 것들]](What Every Computer Scientist Should Know About Floating-Point Arithmetic)〉 ([[전산기학회|ACM]] Computing Surveys, 1991)
 +
 +{{tag>전산학 표준}}

도쿠위키DokuWiki-custom(rev 9085d92e02)을 씁니다.
마지막 수정 2012-09-23 04:26 | 작성자 lifthrasiir