2012-02-17 3 views
3

C++에서이 C 코드 작업 방법을 알려줄 수 있습니까?유니폼 및 이상한 숫자 형식으로 변환

uint64_t flv_dbl2int(double value) 
{ 
    return (union {double f; uint64_t i;}){value}.i;  
} 

나는 무슨 일이 일어나고 있는지 정확히 알지 못한다.

+2

코드가 무엇인지 모르는 경우 왜 사용 하시겠습니까? –

+0

C 스타일의 캐스트 대신'static_cast <>'를 사용하십시오. – iammilind

+0

이 코드는 C 및 C++ 모두에서 정의되지 않은 동작을 일으킨다 고 생각합니다. – Lundin

답변

5

을 "유형 캐스트에 정의되지 않을 수있다", C++에 입력 한 말장난에 대한 유일한 법적 전략은 memcpy() 통해 ,

uint64_t flv_dbl2int(double fvalue) 
{ 
    uint64_t ivalue; 
    memcpy(&ivalue, &fvalue, sizeof ivalue); 
    return ivalue; 
} 

reinterpret_cast

를 사용하여 직선 앞으로 솔루션을 즉
uint64_t flv_dbl2int(double value) 
{ 
    return reinterpret_cast<uint64_t&>(value); 
} 

은 실제로 C 스타일 포인터 캐스트를 사용하는 것과 마찬가지로 정의되지 않은 동작입니다.

C에서 합법적 인 반면, 유니온을 사용하면 은 아마도이며 C++에서는 불법입니다. 실제로 이러한 솔루션은 모두 작동 할 가능성이 있으며 적어도 gcc-4.5.3 및 clang-3.0은 x86에서 -O1에 동일한 코드를 생성합니다.

+0

* 아마도 * C++에서 불법입니다. 다소 모호합니다, 그렇지 않습니까? 나는 C++이 POD의'union '과 같이 제한적이라는 것을 상상할 수 없다. 여기에 관련된 생성자가 없습니다. 반면에, 여러분의 솔루션은 C++에서도'double'과'uint64_t'에 대한 정렬 요구 사항이 다를 수 있기 때문에 * 불법입니다. –

+0

@JensGustedt : 크기가 일치하는 한'memcpy' 메소드는 유효합니다. 두 변수 모두 올바른 정렬을하기 때문에 정렬은 문제가되지 않으며'memcpy'는 바이트를 처리하고 정렬을 신경 쓰지 않습니다. –

+0

@Mike, 아, 미안하지만 충분히 명확하지 않았다. 나는 정렬 문제가있는'reinterpret_cast'를 언급했다. –

0

이 C99 코드는 익명 공용체를 double 값으로 초기화 한 다음 동일한 메모리를 차지하는 uint64_t 값을 반환합니다. 이것은 double을 나타내는 "비트"에 도달하는 방법이지만 권장하지는 않습니다.

실제로 마지막으로 작성된 것과 다른 다른 공용 멤버를 읽는 것은 정의되지 않은 동작입니다.

더 좋은 방법은 그냥 포인터 마법을 사용하려면 수 있습니다 :

uint64_t flv_dbl2int(double value) 
{ 
    return *((uint64_t *) &value); 
} 
+3

아니요, 정의되지 않은 동작이 아닙니다. 정의되지 않은 동작입니다. * 읽은 값이 읽는 유형의 트랩 표현 인 경우. 'uint64_t'는 정의에 의해 결코 트랩 표현을 갖지 않습니다. 반면에'double'과'uint64_t'가 다르게 정렬되어 있다면 당신의 버전은 정의되지 않은 동작을 할 수 있습니다. 그러나 이것은 거의 없습니다. –

+1

@unwind : 당신의 대답은 거꾸로 형식입니다.하지만 유니폼은 추천됩니다 * 포인터 캐스트는 엄격한 앨리어스를 위반하여 정의되지 않은 동작을 호출합니다. – Christoph

3

이는 uint64_tdouble 값을 해석 리터럴 화합물을 사용합니다. 이 구문은 C++에서 사용할 수 없습니다. return 문 앞에 union 유형의 임시 변수를 만들고 해당 변수의 i 필드를 반환하십시오.

uint64_t flv_dbl2int(double value) 
{ 
    union {double f; uint64_t i;} tmp = { value }; 
    return tmp.i;  
} 

편집 :이 코멘트에 일부 주장과 같은 C++에서 "불법"의 경우, 가장 좋은 건 extern "C"로 선언하고 별도의 C (그리고 C++)로 컴파일 아마 컴파일 단위. 어쨌든 이것이 악의적 인 해킹임을 알고 있어야합니다. double은 64 비트 너비가 보장되지 않습니다. 사용자 (또는 상속 된 코드)가 부동 소수점 값의 개별 부분에만 관심이있는 경우 적절한 함수 frexp을 사용하십시오.

를 캐스팅하면서

+0

이것은 C에서 확실히 합법적이지만 C++에 대해서는 확실하지 않습니다. 하나의 액티브 멤버 만 있고 표준에서는 * general *에서 다른 멤버를 활성화하기 위해 명시 적 파괴 및 배치가 필요하다고 말합니다. 그러나 그것은 POD 멤버의 * 특정 * 경우에 필요한지는 분명하지 않지만 표준은 필요한 의미를 지정하지 않으므로 내 생각 엔 C++에서 유니온을 통한 해당 유형의 펀킹이 실제로 불법입니다. – Christoph

+0

@christoph 그것 때문에 내 C++ 프로젝트에서 C 코드 조각을 사용하고 싶습니다. 나머지는 괜찮 았어.하지만. –

+0

'i'는 액세스 할 때 초기화되지 않기 때문에 이것은 C++에서 정의되지 않은 동작을합니다. –

0

그냥 포인터를 사용 기억 : 지금까지 내가 말할 수있는

uint64_t flv_dbl2int(double value) 
{ 
    union Foo {double f; uint64_t i;}; 
    return (*(Foo *)&value).i;  
} 
+0

당신의 솔루션은 긴장을 푸는 것보다 낫지 않습니다. 'double'과'uint64_t'는 이론적으로 다르게 배열 될 수 있고,'Foo'와 같은 것일 수도 있습니다. –