문제

나는 최근에 IEEE 754와 X87 아키텍처에서 상당히 많이 읽었습니다. 나는 내가하고있는 일부 숫자 계산 코드에서 NAN을 "결 측값"으로 사용하려고 생각했고 신호 Nan은 "결 측값"을 진행하고 싶지 않은 경우 부동 소수점 예외를 포착 할 수있게 해줍니다. 반대로, 나는 사용할 것입니다 조용한 NAN은 "결 측값"이 계산을 통해 전파되도록합니다. 그러나 신호 전달 NAS는 내가 존재하는 (매우 제한된) 문서를 기반으로 생각했던 것처럼 작동하지 않습니다.

다음은 내가 아는 내용에 대한 요약입니다 (x87 및 vc ++를 사용 하여이 모든 것).

  • _EM_INVALID (IEEE "Invalid"Exception) NANS를 만날 때 X87의 동작을 제어합니다.
  • _EM_INVALID가 마스킹되면 (예외가 비활성화됨) 예외는 생성되지 않으며 조작이 조용한 NAN을 반환 할 수 있습니다. 신호 NAN과 관련된 작업 ~ 아니다 예외를 던지지 만 조용한 NAN으로 전환됩니다.
  • _EM_INVALID가 마스킹되지 않은 경우 (예외 활성화) 유효하지 않은 작업 (예 : SQRT (-1))로 인해 유효하지 않은 예외가 발생합니다.
  • x87 절대 신호 NAN을 생성합니다.
  • _EM_INVALID가 마스킹되지 않은 경우 어느 신호 NAN을 사용하면 (변수를 초기화하더라도) 잘못된 예외가 발생합니다.

표준 라이브러리는 NAN 값에 액세스하는 방법을 제공합니다.

std::numeric_limits<double>::signaling_NaN();

그리고

std::numeric_limits<double>::quiet_NaN();

문제는 신호 NAN에 전혀 사용하지 않는다는 것입니다. _EM_INVALID가 마스킹되면 조용한 NAN과 정확히 동일하게 작동합니다. NAN은 다른 NAN과 비교할 수 없기 때문에 논리적 차이는 없습니다.

_em_invalid 인 경우 ~ 아니다 마스크 (예외는 활성화 됨), 신호 NAN으로 변수를 초기화 할 수 없습니다.double dVal = std::numeric_limits<double>::signaling_NaN(); 이것은 예외가 발생하기 때문에 (신호 NAN 값은 x87 레지스터에로드되어 메모리 주소에 저장).

당신은 내가 한 것처럼 다음을 생각할 수 있습니다.

  1. 마스크 _EM_INVALID.
  2. 신호 NAN으로 변수를 초기화하십시오.
  3. unmask_em_invalid.

그러나 2 단계는 신호 NAN을 조용한 NAN으로 변환하게하므로 그 후에는 그 후에는 ~ 아니다 예외를 던지게됩니다! 그래서 WTF?!

신호 NAN에 유용성이나 목적이 있습니까? 원래 의도 중 하나는 단위화 된 부동 소수점 값을 사용하여 메모리를 초기화하는 것이 었습니다.

누군가 내가 여기서 뭔가 빠진지 말해 줄 수 있습니까?


편집하다:

내가 원하는 것을 더 설명하기 위해 다음은 다음과 같습니다.

데이터의 벡터 (복식)에서 수학적 작업을 수행하는 것을 고려하십시오. 일부 작업의 경우 벡터가 "결 측값"을 포함하도록 허용하고 싶습니다 (예를 들어 일부 셀에 값이 없지만 존재가 유의 한 스프레드 시트 열에 해당합니다). 일부 작업의 경우, 나는한다 ~ 아니다 벡터에 "결 측값"을 포함하도록 허용합니다. 아마도 "결 측값"이 세트에 존재한다면 다른 조치를 취하고 싶을 것입니다. 아마도 다른 작업을 수행 할 수 있습니다 (따라서 이것은 잘못된 상태가 아닙니다).

이 원본 코드는 다음과 같습니다.

const double MISSING_VALUE = 1.3579246e123;
using std::vector;

vector<double> missingAllowed(1000000, MISSING_VALUE);
vector<double> missingNotAllowed(1000000, MISSING_VALUE);

// ... populate missingAllowed and missingNotAllowed with (user) data...

for (vector<double>::iterator it = missingAllowed.begin(); it != missingAllowed.end(); ++it) {
    if (*it != MISSING_VALUE) *it = sqrt(*it); // sqrt() could be any operation
}

for (vector<double>::iterator it = missingNotAllowed.begin(); it != missingNotAllowed.end(); ++it) {
    if (*it != MISSING_VALUE) *it = sqrt(*it);
    else *it = 0;
}

"결 측값"에 대한 확인을 수행해야합니다. 모든 루프 반복. 나는 대부분의 경우 이해하지만 sqrt 기능 (또는 기타 수학적 조작) 은이 점검을 어둡게 할 수 있습니다. 작업이 최소화되어 있고 (아마도 추가 된 경우) 점검 비용이 많이 듭니다. "결 측값"이 합법적 인 입력 값을 사용하지 않고 계산이 합법적으로 해당 값에 도달하면 버그를 유발할 수 있다는 사실은 말할 것도 없습니다 (가능성은 거의 없음). 또한 기술적으로 정확하기 위해서는 사용자 입력 데이터를 해당 값에 대해 확인해야하며 적절한 작업 과정을 수강해야합니다. 나는이 솔루션이 부적절하고 최적의 성능보다 덜 불가피하다는 것을 알았습니다. 이것은 성능 크리티컬 코드이며, 우리는 분명히 병렬 데이터 구조 또는 데이터 요소 객체의 사치가 없습니다.

NAN 버전은 다음과 같습니다.

using std::vector;

vector<double> missingAllowed(1000000, std::numeric_limits<double>::quiet_NaN());
vector<double> missingNotAllowed(1000000, std::numeric_limits<double>::signaling_NaN());

// ... populate missingAllowed and missingNotAllowed with (user) data...

for (vector<double>::iterator it = missingAllowed.begin(); it != missingAllowed.end(); ++it) {
    *it = sqrt(*it); // if *it == QNaN then sqrt(*it) == QNaN
}

for (vector<double>::iterator it = missingNotAllowed.begin(); it != missingNotAllowed.end(); ++it) {
    try {
        *it = sqrt(*it);
    } catch (FPInvalidException&) { // assuming _seh_translator set up
        *it = 0;
    }
}

이제 명시 적 점검이 제거되고 성능이 향상되어야합니다. FPU 레지스터에 닿지 않고 벡터를 초기화 할 수 있다면 이것이 모두 효과가 있다고 생각합니다 ...

또한, 나는 자존심을 상상할 것입니다 sqrt NAN의 구현 확인 및 NAN을 즉시 반환합니다.

도움이 되었습니까?

해결책

내가 이해 한 바와 같이, NAN 신호의 목적은 데이터 구조를 초기화하는 것이지만 물론 실행 시간 C의 초기화는 초기화의 일부로 NAN을 플로트 레지스터에로드 할 위험이 있습니다. 따라서 컴파일러는 정수 레지스터를 사용 하여이 플로트 값을 복사해야한다는 것을 알지 못하기 때문에 신호를 트리거합니다.

나는 당신이 초기화 할 수 있기를 바랍니다 static 신호 NAN의 가치가 있지만 조용한 NAN으로 변환하지 않도록 컴파일러의 특수 처리가 필요합니다. 초기화 중에 플로트 값으로 취급되는 것을 피하기 위해 약간의 캐스팅 마법을 사용할 수 있습니다.

ASM에 글을 쓰고 있다면 문제가되지 않습니다. 그러나 C와 특히 C ++에서는 NAN과 변수를 초기화하기 위해 유형 시스템을 파괴해야한다고 생각합니다. 사용하는 것이 좋습니다 memcpy.

다른 팁

특수 값 (NULL조차도)을 사용하면 데이터가 훨씬 더 어리석고 코드가 훨씬 더 지저분해질 수 있습니다. QNAN 결과와 QNAN "특별한"값을 구별하는 것은 불가능합니다.

유효성을 추적하기 위해 병렬 데이터 구조를 유지하거나 다른 (SPARS) 데이터 구조에 FP 데이터를 사용하여 유효한 데이터 만 유지하는 것이 좋습니다.

이것은 상당히 일반적인 조언입니다. 특별한 값은 특정 경우에 매우 유용하지만 (예 : 정말 타이트한 메모리 또는 성능 제약 조건) 상황이 커질수록 가치보다 더 어려운 어려움을 유발할 수 있습니다.

비트가 신호 NAN의 비트로 설정된 Const UINT64_T를 가질 수 없습니까? 정수 유형으로 취급하는 한 신호 NAN은 다른 정수와 다르지 않습니다. 포인터 캐스팅을 통해 원하는 곳에 쓸 수 있습니다.

Const uint64_t sNan = 0xfff0000000000000;
Double[] myData;
...
Uint64* copier = (uint64_t*) &myData[index];
*copier=sNan | myErrorFlags;

설정할 비트에 대한 정보 :https://www.doc.ic.ac.uk/~eedwards/compsys/float/nan.html

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top