Lua'nın kaynak kodunu okurken, Lua'nın a'yı 32-bit'e macroyuvarlamak için kullandığını fark ettim . Çıkardım ve şöyle görünüyor:doubleintmacro
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];}
İşte ENDIANLOColarak tanımlanır endian , 0küçük endian için 1büyük endian için. Lua endiyansı dikkatle ele alır. veya tgibi bir tamsayı türü anlamına gelir .intunsigned int
Biraz araştırma yaptım macrove aynı düşünceyi kullanan daha basit bir format var :
#define double2int(i, d) \
{double t = ((d) + 6755399441055744.0); i = *((int *)(&t));}
Veya bir C ++ stilinde:
inline int double2int(double d)
{
d += 6755399441055744.0;
return reinterpret_cast<int&>(d);
}
Bu hile IEEE 754 kullanan herhangi bir makinede çalışabilir (bu hemen hemen her makine anlamına gelir). Hem pozitif hem de negatif sayılar için çalışır ve yuvarlama Bankanın Kuralını takip eder . (Bu şaşırtıcı değildir, çünkü IEEE 754'ü takip eder.)
Test etmek için küçük bir program yazdım:
int main()
{
double d = -12345678.9;
int i;
double2int(i, d)
printf("%d\n", i);
return 0;
}
Beklendiği gibi -12345679 çıktı.
Bu zorluğun nasıl işlediğini ayrıntılı olarak anlatmak istiyorum macro. Sihirli sayı 6755399441055744.0aslında 2^51 + 2^52ya 1.5 * 2^52, ve 1.5ikili olarak temsil edilebilir 1.1. Bu sihirli sayıya herhangi bir 32-bit tam sayı eklendiğinde, buradan kayboldum. Bu hile nasıl çalışır?
Not: Bu Lua kaynak kodunda, Llimits.h .
GÜNCELLEME :
- @Mysticial'ın işaret ettiği gibi, bu yöntem kendini 32 bit ile sınırlamaz , sayı 2 ^ 52 aralığında olduğu sürece
int64 bit'e de genişletilebilirint. (macroBazı değişikliklere ihtiyaç var.) - Bazı malzemeler bu yöntemin Direct3D'de kullanılamayacağını söylüyor .
X86 için Microsoft birleştirici ile çalışırken, daha da hızlı
macroyazılmışassembly(bu da Lua kaynağından çıkarılır):#define double2int(i,n) __asm {__asm fld n __asm fistp i}Tek bir hassas sayı için benzer bir sihirli sayı var:
1.5 * 2 ^23
ftoi. Ama SSE'den bahsediyorsanız, neden sadece tek talimatı kullanmıyorsunuz CVTTSD2SI?
double -> int64çoğu gerçekten 2^52aralık dahilindedir . Bunlar özellikle kayan noktalı FFT'ler kullanarak tamsayı kıvrımları yaparken yaygındır.
