Lua의 소스 코드를 읽을 때 Lua가 a macro
를 사용 double
하여 32 비트로 반올림하는 것을 알았 습니다 int
. 을 추출하면 macro
다음과 같습니다.
union i_cast {double d; int i[2]};
#define double2int(i, d, t) \
{volatile union i_cast u; u.d = (d) + 6755399441055744.0; \
(i) = (t)u.i[ENDIANLOC];}
여기서 엔디안 , 리틀 엔디안, 빅 엔디안ENDIANLOC
으로 정의됩니다 . 루아는 엔디안을 신중하게 처리합니다. 또는 같은 정수 유형을 나타냅니다 .0
1
t
int
unsigned int
나는 약간의 연구를했고 macro
같은 생각을 사용 하는 더 간단한 형식이 있습니다 .
#define double2int(i, d) \
{double t = ((d) + 6755399441055744.0); i = *((int *)(&t));}
또는 C ++ 스타일로 :
inline int double2int(double d)
{
d += 6755399441055744.0;
return reinterpret_cast<int&>(d);
}
이 트릭은 IEEE 754를 사용하는 모든 컴퓨터에서 작동 할 수 있습니다 (현재 거의 모든 컴퓨터를 의미 함). 양수와 음수 모두에서 작동하며 반올림은 Banker 's Rule을 따릅니다 . (이것은 IEEE 754를 따르기 때문에 놀랍지 않습니다.)
나는 그것을 테스트하기 위해 작은 프로그램을 작성했다.
int main()
{
double d = -12345678.9;
int i;
double2int(i, d)
printf("%d\n", i);
return 0;
}
그리고 예상대로 -12345679를 출력합니다.
이 까다로운 macro
작동 방식에 대해 자세히 알고 싶습니다 . 마법의 숫자 6755399441055744.0
는 실제로 2^51 + 2^52
또는 1.5 * 2^52
이며 1.5
이진수로 표시 할 수 있습니다 1.1
. 32 비트 정수 가이 마법 번호에 추가되면 여기서 잃어 버렸습니다. 이 트릭은 어떻게 작동합니까?
추신 : 이것은 Lua 소스 코드 Llimits.h에 있습니다.
업데이트 :
- @Mysticial이 지적 했듯이이 방법은 32 비트로 제한되지 않으며 숫자가 2 ^ 52 범위에있는
int
한 64 비트로 확장 될 수도 있습니다int
. (macro
몇 가지 수정 이 필요합니다.) - 일부 자료에 따르면 Direct3D 에서는이 방법을 사용할 수 없습니다 .
x86 용 Microsoft 어셈블러로 작업 할 때 더 빨리
macro
기록됩니다assembly
(이것은 Lua 소스에서도 추출 됨).#define double2int(i,n) __asm {__asm fld n __asm fistp i}
단정도 숫자와 비슷한 마법 번호가 있습니다.
1.5 * 2 ^23
ftoi
. 그러나 SSE를 사용하고 있다면 왜 단일 명령을 사용하지 CVTTSD2SI
않습니까?
double -> int64
가 실제로 2^52
범위 내에 있습니다. 부동 소수점 FFT를 사용하여 정수 컨볼 루션을 수행 할 때 특히 일반적입니다.