2010-04-20 4 views
4

J'essaie de passer du gros boutiste au petit boutiste sur un double. Une façon de faire est d'utiliserÉchange de bits en C++ pour un double

double val, tmp = 5.55; 

((unsigned int *)&val)[0] = ntohl(((unsigned int *)&tmp)[1]); 
((unsigned int *)&val)[1] = ntohl(((unsigned int *)&tmp)[0]); 

Mais je reçois un avertissement: « pointeur de type punned déréférencement brisera des règles strictes-aliasing » et je ne veux pas tourner cet avertissement au large.

Une autre façon de faire est:

#define ntohll(x) (((uint64_t)(ntohl((uint32_t)((x << 32) >> 32))) << 32) | ntohl(((uint32_t)(x >> 32)))) 

val = (double)bswap_64(unsigned long long(tmp)); //or 
val = (double)ntohll(unsigned long long(tmp)); 

Mais alors perdre les décimales. Quelqu'un sait-il un bon moyen d'échanger les bits sur un double sans utiliser une boucle for?

+1

Si vous faites cela en C++, je crois que vous devriez éviter les lancers de style C. – ereOn

+0

J'ai eu un problème similaire hier, vous pourriez être intéressé par la réponse (http://stackoverflow.com/questions/2667225/endianness-conversion-and-g-warnings) –

+0

J'ai vérifié la réponse à votre question et la solution est ok pour moi, merci – hidayat

Répondre

8

je serais probablement essayer quelque chose comme ceci:

template <typename T> 
void swap_endian(T& pX) 
{ 
    // should static assert that T is a POD 

    char& raw = reinterpret_cast<char&>(pX); 
    std::reverse(&raw, &raw + sizeof(T)); 
} 

court et doux (et relativement non testé). Le compilateur fera toutes les optimisations nécessaires. Ce qui précède est bien défini pour tout type de POD et ne repose sur aucun détail d'implémentation.

Une version de copie, lorsque vous ne voulez pas modifier l'argument:

template <typename T> 
T swap_endian_copy(T pX) 
{ 
    swap_endian(pX); 
    return pX; 
} 
0

Ne pouvez-vous pas les échanger?

inline unsigned long long EndianChange(double d) 
{ 
    char ch[8]; 
    memcpy(ch, &d, 8); // Note this will be optimised out completely by pretty much every compiler. 
    ch[0] ^= ch[7] ^= ch[0] ^= ch[7]; 
    ch[1] ^= ch[6] ^= ch[1] ^= ch[6]; 
    ch[2] ^= ch[5] ^= ch[2] ^= ch[5]; 
    ch[3] ^= ch[4] ^= ch[3] ^= ch[4]; 

    unsigned long long dRet; 
    memcpy(&dRet, ch, 8); // Again this will get optimised out. 
    return dRet; 
}; 

Edit: Comme l'a souligné l'octet permutés double « peut » se charger dans un registre afin que le récupérer sur ce registre peut signifier la valeur n'est plus valide si la stocker dans un long, 64 -bit pour éviter ce problème.

C'est tout ce qu'il y a à un échange d'octets. Je ne suis pas sûr de ce que vous essayez de faire, mais chaque grosse plate-forme endienne que j'ai jamais utilisé utilise le même codage que little-endian juste l'ordre des octets inversé. Le code ci-dessus inversera l'ordre des octets pour vous. Presque tout compilateur fera simplement l'échange d'octets, puis retournera la variable permutée octet et se débarrasser des memcpys. C'est un bon moyen de gérer les problèmes d'alias.

+0

Cette solution ne fonctionne définitivement pas - un double modifié par l'endian n'est probablement plus une valeur FP valide, et provoquera un comportement indéfini dans la FPU. Voir le lien de @Max Lybbert. – stusmith

+0

Bon point ... réparé. – Goz

Questions connexes