En lisant le code source de Lua , j'ai remarqué que Lua utilise a macro
pour 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 ENDIANLOC
se définit comme endianness , 0
pour little endian, 1
pour big endian. Lua gère soigneusement l'endianness. t
représente le type entier, comme int
ou unsigned int
.
J'ai fait un peu de recherche et il existe un format plus simple macro
qui 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.0
est en fait 2^51 + 2^52
, ou 1.5 * 2^52
, et 1.5
en 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 bitsint
tant que le nombre est compris entre 2 ^ 52. (Lemacro
besoin 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 -> int64
sont en effet dans la 2^52
fourchette. Celles-ci sont particulièrement courantes lors de l'exécution de convolutions entières à l'aide de FFT à virgule flottante.