저는 최근에 IEEE 754와 x87 아키텍처에서 꽤 많이 읽었습니다. 내가 노력하고있는 일부 수치 계산 코드에서 "누락 된 값"으로 NaN을 사용하려고 생각하고 있었고 신호로 NaN을 사용하면 내가 아닌 곳에서 부동 소수점 예외를 잡을 수 있었으면 좋겠다. "누락 된 값"으로 진행하고 싶습니다. 반대로, 을 사용하면 "누락 된 값"이 계산을 통해 전파되도록 허용하기 위해 NaN을 사용합니다. 그러나 시그널링 NaN은 그들이 존재하는 (매우 제한적인) 문서를 기반으로한다고 생각했기 때문에 작동하지 않습니다. 여기 신호 NaN의 유용성?
내가 알고있는 것을 요약 한 것입니다 (이 사용하는 x87 및 VC++의 모든) :- _EM_INVALID합니다 (IEEE "무효"예외) 경우 NaN을
- 가 발생 x87의 동작을 제어 _EM_INVALID는 마스크 처리됩니다 (예외는 무효). 예외는 생성되지 않고, 오퍼레이션은 조용한 NaN를 돌려 줄 수가 있습니다. NaN 시그널을 포함하는 조작은 이 아니고,가 예외를 Throw하지만, NaN에 변환됩니다.
- _EM_INVALID가 마스크 해제 된 경우 (예외 사용) 잘못된 조작 (예 : sqrt (-1))으로 인해 잘못된 예외가 발생합니다.
- x87 결코은 신호 NaN을 생성합니다.
- _EM_INVALID가 마스크 해제 된 경우 신호 NaN을 사용하면 (심지어 변수를 초기화해도) 잘못된 예외가 발생합니다.
std::numeric_limits<double>::signaling_NaN();
및
std::numeric_limits<double>::quiet_NaN();
문제는 내가 신호 NaN이에 대해 어떠한 사용을 볼 수 없다는 것입니다 :
는 표준 라이브러리는 NaN의 값에 액세스 할 수있는 방법을 제공합니다. _EM_INVALID가 마스크되어 있으면 조용한 NaN과 똑같이 동작합니다. NaN은 다른 NaN과 비교할 수 없기 때문에 논리적 인 차이가 없습니다.
_EM_INVALID 인 경우 하지 마스크 (예외가 활성화) 후 하나도 시그널링 NaN를 가진 변수를 초기화 할 수있다 :이 (시그널링 NaN의 가치가 x87에로드를 저장하는 레지스터는 예외가 발생하기 때문에 double dVal = std::numeric_limits<double>::signaling_NaN();
을 메모리 주소로). 내가 그랬던 것처럼
는 다음을 생각할 수 있습니다 :
- 마스크 _EM_INVALID을.
- 신호 NaN을 사용하여 변수를 초기화하십시오.
- 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 신호를 보았던 유일한 용도는 토요일 오후 9시 30 분에 휴대 전화로 전화를 걸 수 있습니다. –