programing

C/C++의 더블을 통해 운반할 때 Float의 보존이 보장됩니까?

subpage 2023. 10. 26. 21:03
반응형

C/C++의 더블을 통해 운반할 때 Float의 보존이 보장됩니까?

IEEE-754 적합성을 가정할 때, 이중으로 운반할 때 플로트는 반드시 보존되어야 합니까?

즉, 다음과 같은 주장은 항상 충족될 것인가요?

int main()
{
    float f = some_random_float();
    assert(f == (float)(double)f);
}

라고 가정합니다.fNaN 및 인피니티와 같이 IEEE에서 정의한 특수 값을 획득할 수 있습니다.

IEEE에 따르면, 이중을 통한 전송 후 정확한 비트 레벨 표현이 보존되지 않는 경우가 있습니까?

코드 조각은 C와 C++에서 모두 유효합니다.

IEEE를 가정할 필요도 없습니다.C89는 3.1.2.5에서 다음과 같이 말합니다.

유형의 값 집합float는 유형의 값 집합의 부분 집합입니다.double

그리고 다른 모든 C와 C++ 표준은 동등한 것을 말합니다.NaN과 무한은 "유형의 값"으로 알고 있습니다.float", 피연산자로 사용할 때 일부 특수한 경우 규칙이 포함된 값입니다.

-> -> -> 이중 -> 플로트 플로트 의됩니다의 .float는 (일반적으로) 대상 유형에서 값을 나타낼 수 있는 경우 숫자 변환이 모두 값을 보존한다는 사실에서 따옵니다.

비트 레벨 표현은 약간 다른 문제입니다.여기에 중요한 가치가 있다고 생각해보세요.float두 개의 뚜렷한 비트 와이즈 표현이 있습니다.그러면 C 표준의 어떤 것도 플로트 -> 더블 -> 플로트 변환을 방해하지 않습니다.IEEE에서는 패딩 비트(padding bit)가 없는 한 "실제 값"에 대해서는 발생하지 않지만, IEEE에서 비트 단위로 표현되는 단일 NaN을 배제하는지는 알 수 없습니다.NaN은 자신과 동일한 것이 아니므로 두 NaN이 "동일한 NaN"인지 "다른 NaN"인지 구별하는 표준 방법은 문자열로 변환하는 것 외에는 없습니다.문제가 해결되지 않을 수 있습니다.

주의해야 할 한 가지는 컴파일러의 부적합한 모드로, 예를 들어 중간 결과물이 부동 소수점 레지스터에 남아 반올림 없이 재사용되는 등 매우 정확한 값을 "덮개 아래"로 유지하는 것입니다.그렇다고 해서 예시 코드가 실패할 것 같지는 않지만, 부동 소수점을 사용하는 순간==당신이 걱정하기 시작하는 그런 종류의 것입니다.

C99부터:

6.3.1.5 실제 부유식
1 플로트가 더블 또는 롱 더블로 승격되거나 롱 더블로 승격될 때 그 가치는 변하지 않습니다.
2 더블이 플로팅으로 강등될 때, 긴 더블이 더블 또는 플로팅으로 강등되거나, 의미 유형에서 요구되는 것보다 더 큰 정밀도와 범위로 표현되는 값(6.3.1.8 참조)이 명시적으로 의미 유형으로 변환되는 경우, 변환되는 값이 정확히 새로운 유형으로 표현될 수 있다면 변경되지 않습니다.

저는 이것이 플로트->더블->플로트 변환이 원래의 플로트 값을 보존한다는 것을 보장한다고 생각합니다.

표준은 또한 매크로를 정의합니다.INFINITY그리고.NAN인에7.12 Mathematics <math.h>:

4 매크로 인피니티는 양 또는 부호가 없는 무한대를 나타내는 유형 플로트의 일정한 표현으로 확장되고, 그렇지 않은 경우 변환 시 오버플로되는 유형 플로트의 양의 상수로 확장됩니다.
5 매크로 NAN은 구현이 플로트 유형에 대해 조용한 NaN을 지원하는 경우에만 정의됩니다.그것은 조용한 NaN을 나타내는 타입 플로트의 일정한 표현으로 확장됩니다.

따라서, 그러한 특별한 값에 대한 규정이 있으며 변환은 (마이너스 무한대와 마이너스 0을 포함하여) 그 값들에도 적용될 수 있습니다.

f가 정규화되지 않으면 플러시-제로 모드 및/또는 정규화되지 않은-is-zero 모드(예: - mfpmath=sse, -fast-math 등으로 컴파일된 코드)에서 어설션이 실패하지만, 인텔의 C++ 컴파일러와 같은 수많은 컴파일러와 아키텍처에서도 기본값으로 실패합니다.

이 모드에서는 정규화되지 않은 플로트를 생성할 수 없지만 시나리오는 여전히 가능합니다.

a) 탈규격화된 부유물은 외부 소스에서 옵니다.

b) 일부 라이브러리는 FPU 모드를 조작하지만 각 함수가 호출할 때마다 FPU 모드를 다시 설정하는 것을 잊어버리거나 의도적으로 설정하지 않으므로 호출자가 정규화를 일치시키지 않을 수 있습니다.

다음을 인쇄하는 실제 예시:

f = 5.87747e-39
f2 = 5.87747e-39

f = 5.87747e-39
f2 = 0
error, f != f2!

예제는 VC2010 및 GCC 4.3에 대해 모두 작동하지만 VC는 수학용 SSE를 기본값으로 사용하고 GCC는 수학용 FPU를 기본값으로 사용한다고 가정합니다.예제는 그렇지 않으면 문제를 설명하지 못할 수 있습니다.

#include <limits>
#include <iostream>
#include <cmath>

#ifdef _MSC_VER
#include <xmmintrin.h>
#endif

template <class T>bool normal(T t)
{
    return (t != 0 || fabsf( t ) >= std::numeric_limits<T>::min());
}

void csr_flush_to_zero()
{
#ifdef _MSC_VER
    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
#else
    unsigned csr = __builtin_ia32_stmxcsr();
    csr |= (1 << 15);
    __builtin_ia32_ldmxcsr(csr);
#endif
}

void test_cast(float f) 
{
    std::cout << "f = " << f << "\n";
    double d = double(f);
    float f2 = float(d);
    std::cout << "f2 = " << f2 << "\n";

    if(f != f2)
        std::cout << "error, f != f2!\n";

    std::cout << "\n";
}

int main()
{
    float f = std::numeric_limits<float>::min() / 2.0;

    test_cast(f);
    csr_flush_to_zero();
    test_cast(f);
}

언급URL : https://stackoverflow.com/questions/14773142/is-a-float-guaranteed-to-be-preserved-when-transported-through-a-double-in-c-c

반응형