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
답변
A double
는 다음과 같이 표현됩니다 :
두 개의 32 비트 정수로 볼 수 있습니다. 이제 int
코드의 모든 버전에서 찍은 (32 비트라고 가정 int
)은 그림의 오른쪽에있는 것이므로 결국 32 비트의 가수를 취하는 것입니다.
이제 매직 넘버로; 올바르게 언급했듯이 6755399441055744는 2 ^ 51 + 2 ^ 52입니다. 이러한 숫자를 추가하면 double
2 ^ 52에서 2 ^ 53 사이의 “달콤한 범위”로 들어가게됩니다. 여기 Wikipedia 에서 설명했듯이 흥미로운 속성이 있습니다.
2 사이 52 = 4,503,599,627,370,496과 2 53 = 9,007,199,254,740,992 사이에서 표현 가능한 숫자는 정확히 정수입니다
가수는 52 비트 폭이라는 사실에서 비롯됩니다.
2 51 추가에 대한 다른 흥미로운 사실 +2 52를 우리가 가장 낮은 32 비트만을 취하기 때문에 어쨌든 버려지는 두 개의 최상위 비트에서만 가수에 영향을 미친다는 것입니다.
마지막으로 : 부호.
IEEE 754 부동 소수점은 크기와 부호 표현을 사용하는 반면 “정상”기계의 정수는 2의 보수 산술을 사용합니다. 여기서 어떻게 처리합니까?
우리는 양의 정수에 대해서만 이야기했습니다. 이제 32 비트로 표현할 수있는 범위에서 음수를 처리한다고 가정하면 int
(-2 ^ 31 + 1)보다 절대 값이 적습니다. 호출 -a
. 이러한 숫자는 매직 넘버를 추가하여 분명히 양수이며 결과 값은 2 52 +2 51 + ( -a )입니다.
이제 가수를 2의 보수 표현으로 해석하면 무엇을 얻을 수 있습니까? 2의 보수 합 (2 52 +2 51 )과 (-a) 의 결과 여야합니다 . 다시 말하지만, 첫 번째 항은 상위 2 비트에만 영향을 미치며, 0 ~ 50 비트에 남아있는 것은 2의 보수 표현 (-a)입니다 (다시, 상위 2 비트 빼기).
왼쪽의 여분의 비트를 잘라 냄으로써 2의 보수 수를 더 작은 폭으로 줄이는 것이 이루어 지므로, 더 낮은 32 비트를 취하면 32 비트, 2의 보수 산술에서 정확하게 (-a)가됩니다.