Lua'nın kaynak kodunu okurken, Lua'nın a'yı 32-bit'e macro
yuvarlamak için kullandığını fark ettim . Çıkardım ve şöyle görünüyor:double
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];}
İşte ENDIANLOC
olarak tanımlanır endian , 0
küçük endian için 1
büyük endian için. Lua endiyansı dikkatle ele alır. veya t
gibi bir tamsayı türü anlamına gelir .int
unsigned int
Biraz araştırma yaptım macro
ve 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.0
aslında 2^51 + 2^52
ya 1.5 * 2^52
, ve 1.5
ikili 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
int
64 bit'e de genişletilebilirint
. (macro
Bazı 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ı
macro
yazı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^52
aralık dahilindedir . Bunlar özellikle kayan noktalı FFT'ler kullanarak tamsayı kıvrımları yaparken yaygındır.