| |||
| Home > 부동 소수점 지원 > 부동 소수점 환경 제어 > ARM 컴파일러 C99 인터페이스 확장 | |||
ARM 컴파일러는 C99 인터페이스에 확장을 제공하여 ARM 부동 소수점 환경에서 가능한 모든 것을 C99에서도 할 수 있도록 합니다. 이러한 확장에는 개별 예외 유형의 트래핑 및 언트래핑은 물론 사용자 정의 트랩 처리기 설치도 포함됩니다.
C99은 fenv_t 및 fexcept_t 유형을
특별한 유형으로 정의하지 않습니다. ARM 컴파일러는 이 두 유형이 동일한 구조 유형을 갖도록 정의합니다.
typedef struct { unsigned statusword; __ieee_handler_t __invalid_handler; __ieee_handler_t __divbyzero_handler; __ieee_handler_t __overflow_handler; __ieee_handler_t __underflow_handler; __ieee_handler_t __inexact_handler; } fenv_t, fexcept_t;
이 구조의 구성원은 다음과 같습니다.
statusword는 __ieee_status 함수가
보는 것과 동일한 상태 변수이며 동일한 형식으로 배치됩니다(__ieee_status() 참조).
5개의 함수 포인터가 각 예외에 대해 트랩 처리기 주소를 제공합니다. 기본적으로 각 함수
포인터는 NULL입니다. 이는 예외가 트랩될 경우 기본 예외 트랩 조치가 발생한다는
것을 의미합니다. 기본값은 SIGFPE 신호를 발생시키는
것입니다.
사용자 정의 예외 트랩 처리기를 설치하려면 다음과 같은 함수로 선언하십시오.
__softfp__ieee_value_t myhandler(__ieee_value_t op1,
__ieee_value_t op2,
__ieee_edata_t edata);
이 함수에 대한 매개변수:
op1 및 op2는
예외를 발생시킨 연산에 대해 피연산자 또는 중간 결과를 제공하는 데 사용됩니다.
잘못된 연산과 0으로 나누기 예외의 경우 원래의 피연산자가 제공됩니다.
정확하지 않은 결과 예외의 경우 제공된 모든 결과는 반환된 일반적인 결과입니다. 이 결과는 op1에
제공됩니다.
오버플로 예외의 경우 중간 결과가 제공됩니다. 이 결과는 지수 범위가 충분히 컸을 경우에
연산이 반환할 결과를 산출한 후, 지수를 형식에 맞도록 조정하여 계산됩니다. 지수는 단정밀도에서는 192(0xC0),
배정밀도에서는 1536(0x600에 의해 조정됩니다.
double을 float으로 변환할 때 오버플로가 발생하면, 결과는 단정밀도로 반올림되어 double 형식으로 제공되며, 이 때 지수는 192로 바이어스됩니다.
언더플로 예외의 경우, 유사한 중간 결과가 생성되지만 바이어스 값은 빼지 않고 오히려
지수에 더해집니다. edata 매개변수에는 중간 결과를 반올림 또는 반내림했어야
하는지, 아니면 아무 조치도 취하지 말아야 했는지를 알려주는 플래그도 포함되어 있습니다.
__ieee_value_t 유형은 피연산자를 다음과 같이 전달할 수 있는
모든 유형(가능한 모든 잠재 유형)의 공용체로 정의됩니다.
typedef union { float __f; float __s; double __d; int __i; unsigned int __ui; #if !defined(__STRICT_ANSI__) || (defined(__STDC_VERSION__) && 199901L <= __STDC_VERSION__) long long __l; unsigned long long __ul; #endif /* __STRICT_ANSI__ */ struct { int __word1, __word2; } __str; } __ieee_value_t; /* in/out values passed to traps */
--strict를 사용하여 컴파일하지 않고 __ieee_value_t의
기존 정의를 사용한 코드가 있을 경우 기존 코드는 여전히 작동합니다. 자세한 내용은 fenv.h를 참조하십시오.
edata에는 발생한 예외 및 어떤 연산이 수행되었는지에
대한 정보를 제공하는 플래그가 있습니다. __ieee_edata_t 유형은 unsigned
int의 동의어입니다.
함수의 반환 값은 예외가 일어난 연산의 결과로서 사용됩니다.
edata에 포함된 플래그는 다음과 같습니다.
edata & FE_EX_RDIR은
언더플로의 중간 결과가 반내림되었을 경우에 0이 아닌 수가 되며, 반올림되었거나 반올림되지 않았을 경우에는 0이 됩니다.
마지막 두 개의 차이는 정확하지 않은 결과 비트에 제공됩니다. 이 비트는 다른 유형의 예외에 대해서는 아무 의미도
없습니다.
edata & FE_EX_은
해당 exception(예외INVALID, DIVBYZERO, OVERFLOW, UNDERFLOW 또는 INEXACT)가
발생하면 0이 아닌 수가 됩니다. 따라서 다음 작업을 할 수 있습니다.
두 개 이상의 예외 유형에 동일한 처리기 함수를 사용합니다. 이 함수는 이러한 비트를 테스트하여 어떤 예외를 처리해야 하는지 확인할 수 있습니다.
오버플로 및 언더플로 중간 결과가 반올림되었는지 아니면 정확한지 확인합니다.
FE_EX_INEXACT 비트는 FE_EX_OVERFLOW 또는 FE_EX_UNDERFLOW와
조합하여 설정될 수 있으므로 정확하지 않은 결과를 테스트하기 전에 오버플로와 언더플로를 테스트하여 실제로 발생한 예외
유형을 확인해야 합니다.
edata & FE_EX_FLUSHZERO는
연산 수행 시 FZ 비트가 설정된 경우 0이 아닌 수가 됩니다(__ieee_status() 참조).
edata & FE_EX_ROUND_MASK는
연산에 적용되는 반올림 모드를 제공합니다. 예외를 초래한 연산이 항상 0으로 반올림되는 _ffix와
같은 루틴이 아닌 한 이것은 일반적으로 현재 반올림 모드와 동일합니다. 사용할 수 있는 반올림 모드 값은 FE_EX_ROUND_NEAREST, FE_EX_ROUND_PLUSINF, FE_EX_ROUND_MINUSINF 및 FE_EX_ROUND_ZERO입니다.
edata & FE_EX_INTYPE_MASK는 Table 4.9에 제시된 유형 값 중 하나를
함수에 피연산자의 유형으로 제공합니다.
edata & FE_EX_OUTTYPE_MASK는 Table 4.10에 제시된 유형 값 중 하나를
함수에 피연산자의 유형으로 제공합니다.
edata & FE_EX_FN_MASK는 Table 4.11에 제시된 연산 코드 중 하나를
예외를 초래한 연산의 특성으로 제공합니다.
Table 4.11. FE_EX_FN_MASK 연산 유형 플래그
| 플래그 | 연산 유형 |
|---|---|
FE_EX_FN_ADD | 더하기 |
FE_EX_FN_SUB | 빼기 |
FE_EX_FN_MUL | 곱하기 |
FE_EX_FN_DIV | 나누기 |
FE_EX_FN_REM | 나머지 |
FE_EX_FN_RND | 정수로 반올림 |
FE_EX_FN_SQRT | 제곱근 |
FE_EX_FN_CMP | 비교 |
FE_EX_FN_CVT | 형식 간 변환 |
FE_EX_FN_LOGB | 지수 가져오기 |
FE_EX_FN_SCALBN | 스케일링 Note
|
FE_EX_FN_NEXTAFTER | 다음 표현 값 Note두 피연산자의 유형은 동일합니다. |
FE_EX_FN_RAISE | feraiseexcept 또는 feupdateenv에
의해 예외가 명시적으로 발생되었습니다. 이 경우 edata 워드 내의 것은 거의
대부분 유효하지 않습니다. |
비교 연산일 경우 결과는 int인 것처럼 반환되어야 하며, Table 4.12에 제시된 4가지 값 중 하나여야 합니다.
입력 및 출력 유형은 비교와 변환을 제외하고 모든 연산에서 동일합니다.
Example 4.1은 사용자 정의 예외 처리기를 보여 줍니다. 특정 Fortran 코드를 C로 변환하고 있다고 가정하십시오. Fortran 숫자 표준에서는 0 나누기 0을 1로 규정하고 있는 반면, IEEE 754에서는 0 나누기 0을 잘못된 연산으로 정의하고 있으므로 기본적으로 Quiet NaN이 반환됩니다. Fortran 코드에서는 0 나누기 0이 1을 반환하는 것이 코드를 수정하는 것보다 용이하므로 이 동작에 의존할 가능성이 높습니다.
컴파일할 때 --fpmode=ieee_full 또는 --fpmode=ieee_fixed와
같은 예외를 지원하는 부동 소수점 모델을 선택해야 합니다.
처리기를 설치한 후 0.0을 0.0으로 나누면 1.0이 반환됩니다.
Example 4.1. 사용자 정의 예외 처리기
#include <fenv.h>
#include <signal.h>
#include <stdio.h>
__softfp __ieee_value_t myhandler(__ieee_value_t op1, __ieee_value_t op2,
__ieee_edata_t edata)
{
__ieee_value_t ret;
if ((edata & FE_EX_FN_MASK) == FE_EX_FN_DIV)
{
if ((edata & FE_EX_INTYPE_MASK) == FE_EX_INTYPE_FLOAT)
{
if (op1.f == 0.0 && op2.f == 0.0)
{
ret.f = 1.0;
return ret;
}
}
if ((edata & FE_EX_INTYPE_MASK) == FE_EX_INTYPE_DOUBLE)
{
if (op1.d == 0.0 && op2.d == 0.0)
{
ret.d = 1.0;
return ret;
}
}
}
/* For all other invalid operations, raise SIGFPE as usual */
raise(SIGFPE);
}
int main(void)
{
float i, j, k;
fenv_t env;
fegetenv(&env);
env.statusword |= FE_IEEE_MASK_INVALID;
env.invalid_handler = myhandler;
fesetenv(&env);
i = 0.0;
j = 0.0;
k = i/j;
printf("k is %f\n", k);
}
예외가 트랩되어도 트랩 처리기 주소가 NULL로 설정되어 있으면 기본
트랩 처리기가 사용됩니다.
기본 트랩 처리기는 SIGFPE 신호를 발생시킵니다. SIGFPE의 기본 처리기는 오류 메시지를 표시하고 프로그램을 종료시킵니다.
SIGFPE를 트랩할 경우 발생한 부동 소수점
예외의 유형을 알려주는 두 번째 매개변수를 갖도록 신호 처리기 함수를 선언할 수 있습니다. 이 기능은 Microsoft
제품과의 호환성을 위해 제공됩니다. 값은 _FPE_INVALID, _FPE_ZERODIVIDE, _FPE_OVERFLOW, _FPE_UNDERFLOW 및 _FPE_INEXACT입니다.
이들 값은 float.h에 정의되어 있습니다. 예를 들어 다음과 같습니다.
void sigfpe(int sig, int etype) { printf("SIGFPE (%s)\n", etype == _FPE_INVALID ? "Invalid Operation" : etype == _FPE_ZERODIVIDE ? "Divide by Zero" : etype == _FPE_OVERFLOW ? "Overflow" : etype == _FPE_UNDERFLOW ? "Underflow" : etype == _FPE_INEXACT ? "Inexact Result" : "Unknown"); } signal(SIGFPE, (void(*)(int))sigfpe);
이 추가 정보를 사용하여 직접 SIGFPE 신호를
생성하려면 ISO 함수 raise() 대신 __rt_raise 함수를
호출합니다. Example 4.1에서
다음 코드 대신,
raise(SIGFPE);
다음과 같이 코드를 작성하는 것이 좋습니다.
__rt_raise(SIGFPE, _FPE_INVALID);
__rt_raise는 rt_misc.h에
선언되어 있습니다.