En lisant le code source de Lua , j'ai remarqué que Lua utilise a macropour arrondir a doubleà 32 bits int. J'ai extrait le macro, et cela ressemble à ceci:
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];}
Ici ENDIANLOCse définit comme endianness , 0pour little endian, 1pour big endian. Lua gère soigneusement l'endianness. treprésente le type entier, comme intou unsigned int.
J'ai fait un peu de recherche et il existe un format plus simple macroqui utilise la même pensée:
#define double2int(i, d) \
{double t = ((d) + 6755399441055744.0); i = *((int *)(&t));}
Ou dans un style C ++:
inline int double2int(double d)
{
d += 6755399441055744.0;
return reinterpret_cast<int&>(d);
}
Cette astuce peut fonctionner sur n'importe quelle machine utilisant IEEE 754 (ce qui signifie à peu près toutes les machines aujourd'hui). Cela fonctionne pour les nombres positifs et négatifs, et l'arrondi suit la règle du banquier . (Ce n'est pas surprenant, car il suit IEEE 754.)
J'ai écrit un petit programme pour le tester:
int main()
{
double d = -12345678.9;
int i;
double2int(i, d)
printf("%d\n", i);
return 0;
}
Et il produit -12345679, comme prévu.
Je voudrais entrer dans les détails sur le fonctionnement de cette astuce macro. Le nombre magique 6755399441055744.0est en fait 2^51 + 2^52, ou 1.5 * 2^52, et 1.5en binaire peut être représenté par 1.1. Lorsqu'un entier 32 bits est ajouté à ce nombre magique, eh bien, je suis perdu d'ici. Comment fonctionne cette astuce?
PS: C'est dans le code source de Lua, Llimits.h .
MISE À JOUR :
- Comme le souligne @Mysticial, cette méthode ne se limite pas à 32 bits
int, elle peut également être étendue à 64 bitsinttant que le nombre est compris entre 2 ^ 52. (Lemacrobesoin de quelques modifications.) - Certains matériaux indiquent que cette méthode ne peut pas être utilisée dans Direct3D .
Lorsque vous travaillez avec l'assembleur Microsoft pour x86, il existe une
macroécriture encore plus rapideassembly(elle est également extraite de la source Lua):#define double2int(i,n) __asm {__asm fld n __asm fistp i}Il existe un nombre magique similaire pour un nombre à précision unique:
1.5 * 2 ^23
ftoi. Mais si vous parlez SSE, pourquoi ne pas simplement utiliser l'instruction unique CVTTSD2SI?
double -> int64sont en effet dans la 2^52fourchette. Celles-ci sont particulièrement courantes lors de l'exécution de convolutions entières à l'aide de FFT à virgule flottante.
