Come faccio a convertire tra valori big-endian e little-endian in C ++?


196

Come faccio a convertire tra valori big-endian e little-endian in C ++?

EDIT: Per chiarezza, devo tradurre i dati binari (valori in virgola mobile a precisione doppia e numeri interi a 32 e 64 bit) da un'architettura CPU all'altra. Ciò non implica il networking, quindi ntoh () e funzioni simili non funzioneranno qui.

EDIT # 2: la risposta che ho accettato si applica direttamente ai compilatori che sto prendendo di mira (motivo per cui l'ho scelto). Tuttavia, ci sono altre risposte molto buone e più portatili qui.


21
ntoh hton funzionerà bene, anche se non ha nulla a che fare con il networking.
Ben Collins,

2
Il modo migliore per gestire l'endianness in generale è assicurarsi che il codice sia eseguito su macchine host sia little che big endian. Se funziona, probabilmente hai fatto bene. Supponendo che tu sia su x86 / be è pericoloso come pratica.
jakobengblom2,

10
hton ntoh non funzionerà se la macchina è big-endian, perché la domanda chiesta vuole esplicitamente eseguire la conversione.
fabspro,

6
@ jakobengblom2 è l'unica persona a menzionarlo. Quasi tutti gli esempi in questa pagina usano concetti come "scambiare" byte invece di farlo agnostico dell'endianità sottostante. Se hai a che fare con formati di file esterni (che hanno endianness ben definite), la cosa più portatile da fare è trattare i dati esterni come un flusso di byte e convertire il flusso di byte in e da numeri interi nativi. Io rabbrividisco ogni volta che vedo short swap(short x)codice, poiché si romperà se ti sposti su una piattaforma con endianness diverse. Matthieu M ha l'unica risposta giusta qui sotto.
Mark Lakata

3
Stai pensando al problema completamente sbagliato. Il compito non è "come faccio a convertire tra valori big-endian e little-endian". Il compito è "come posso convertire i valori in virgola mobile e interi in un formato particolare nel formato nativo della mia piattaforma". Se lo fai nel modo giusto, il formato nativo può essere big endian, little endian, mixed endian o ternary per tutte le preoccupazioni del tuo codice.
David Schwartz,

Risposte:


166

Se si utilizza Visual C ++ , attenersi alla seguente procedura: Includere intrin.h e chiamare le seguenti funzioni:

Per numeri a 16 bit:

unsigned short _byteswap_ushort(unsigned short value);

Per numeri a 32 bit:

unsigned long _byteswap_ulong(unsigned long value);

Per numeri a 64 bit:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

I numeri a 8 bit (caratteri) non devono essere convertiti.

Inoltre, questi sono definiti solo per valori senza segno e funzionano anche per numeri interi con segno.

Per float e doppi è più difficile come con interi semplici in quanto questi possono essere o meno nell'ordine di byte delle macchine host. Puoi ottenere float little-endian su macchine big-endian e viceversa.

Anche altri compilatori hanno intrinsechi simili.

In GCC, ad esempio, puoi chiamare direttamente alcuni builtin come documentato qui :

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(non c'è bisogno di includere qualcosa). Afaik bits.h dichiara la stessa funzione anche in modo non gcc-centrico.

Scambio a 16 bit è solo una rotazione dei bit.

Chiamare gli intrinseci invece di farli rotolare da soli ti dà le migliori prestazioni e densità di codice tra l'altro.


11
Con GCC, potrei usare: #include <byteswap.h> int32_t bswap_32 (int32_t x) int64_t bswap_64 (int64_t x)
jmanning2k

5
__builtin_bswapXè disponibile solo da GCC-4.3 in poi
Matt Joiner il

20
E 'anche interessante notare che queste intrinseche / sempre / swap byte, non come sono htonl, htonse così via è necessario conoscere dal contesto della vostra situazione quando in realtà scambiare i byte.
Brian Vandenberg,

8
@Jason perché i numeri a 8 bit sono gli stessi in big e little endian. :-)
Nils Pipenbrinck,

2
@BrianVandenberg Right; usare htonle ntohlsenza preoccuparsi del contesto funzionerebbe durante la scrittura di codice portatile poiché la piattaforma che definisce queste funzioni lo cambierebbe se fosse piccolo / medio-endiano e su big-endian sarebbe un no-op. Tuttavia, quando si decodifica un tipo di file standard che è definito come little-endian (diciamo BMP), si deve ancora conoscere il contesto e non si può semplicemente fare affidamento su htonle ntohl.
legends2k,

86

In poche parole:

#include <climits>

template <typename T>
T swap_endian(T u)
{
    static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");

    union
    {
        T u;
        unsigned char u8[sizeof(T)];
    } source, dest;

    source.u = u;

    for (size_t k = 0; k < sizeof(T); k++)
        dest.u8[k] = source.u8[sizeof(T) - k - 1];

    return dest.u;
}

utilizzo: swap_endian<uint32_t>(42).


3
Avere un voto. Ho appena usato uchars e assegnato 4 a 1, 3 a 2, 2 a 3 e 1 a 4, ma questo è più flessibile se hai dimensioni diverse. 6 orologi su un Pentium IIRC di prima generazione. BSWAP è 1 orologio, ma è specifico della piattaforma.

2
@RocketRoy: Sì, e se la velocità si rivela un problema, è molto semplice scrivere sovraccarichi con intrisica specifica per piattaforma e tipo.
Alexandre C.

3
@MihaiTodor: questo uso dei sindacati per la tipografia attraverso una serie di caratteri è esplicitamente consentito dallo standard. Vedi ad es. questa domanda .
Alexandre C.

4
@AlexandreC. Non nello standard C ++ - solo in C. In C ++ (che è questo codice) questo codice è un comportamento indefinito.
Rapptz,

4
@Rapptz: 3.10 sembra chiaro: "Se un programma tenta di accedere al valore memorizzato di un oggetto attraverso un valore glaciale diverso da uno dei seguenti tipi, il comportamento non è definito: [...] un carattere char o un tipo char senza segno. ". Forse mi manca qualcosa qui, ma mi è stato abbastanza chiaro che l'accesso a qualsiasi tipo tramite i caratteri char è stato esplicitamente consentito.
Alexandre C.,

75

Da The Byte Order Fallacy di Rob Pike:

Supponiamo che il tuo flusso di dati abbia un numero intero a 32 bit con codifica little-endian. Ecco come estrarlo (assumendo byte non firmati):

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

Se è big-endian, ecco come estrarlo:

i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

TL; DR: non preoccuparti del tuo ordine nativo della piattaforma, tutto ciò che conta è l'ordine di byte dello stream da cui stai leggendo e speri che sia ben definito.

Nota: nel commento è stato osservato che in assenza di una conversione esplicita del tipo, era importante che datafosse una matrice di unsigned charo uint8_t. L'uso di signed charo char(se firmato) comporterà la data[x]promozione a un numero intero e il data[x] << 24potenziale spostamento di 1 nel bit di segno che è UB.


6
Questo è bello, ma mi sembra che si applichi solo agli interi e alle varianti. Cosa fare con float / doppi?
Brett,

1
@ v.oddou: sì e no, i file mappati in memoria sono esattamente gli stessi dei frame di rete; se accetti di non leggerli direttamente, tutto ciò che conta è la loro endianness: se little-endian, usa la prima formula, se è big-endian, usa la seconda. Qualsiasi compilatore degno di nota ottimizzerà le trasformazioni non necessarie se l'endianness corrisponde.
Matthieu M.

2
@meowsqueak: Sì, mi aspetto che funzioni, perché cambia solo l'ordine dei byte, non l'ordine dei bit all'interno di ogni byte.
Matthieu M.,

3
Su una nota vagamente correlata, il post collegato è una lettura spiacevole ... Il ragazzo sembra apprezzare la brevità, ma ha preferito scrivere un lungo sfogo su tutti quei cattivi programmatori che non sono così illuminati come lo è per quanto riguarda l'endianità, invece che effettivamente spiegando la situazione e PERCHÉ la sua soluzione funziona sempre.
Annuncio N

1
Se stai usando questo metodo, assicurati di trasmettere i tuoi dati a (unsigned char *)
joseph

51

Se si esegue questa operazione ai fini della compatibilità di rete / host, è necessario utilizzare:

ntohl() //Network to Host byte order (Long)
htonl() //Host to Network byte order (Long)

ntohs() //Network to Host byte order (Short)
htons() //Host to Network byte order (Short)

Se lo stai facendo per qualche altro motivo, una delle soluzioni byte_swap presentate qui funzionerebbe perfettamente.


2
l'ordinamento dei byte di rete è big endian credo. Queste funzioni possono essere utilizzate tenendo presente ciò anche se non si utilizza il codice di rete. Tuttavia non ci sono versioni float ntohf o htonf
Matt

2
Matt H. è solo per lo più corretto. Non tutti i sistemi di computer hanno un ordine di byte little-endian. Se stavi lavorando, ad esempio una motorolla 68k, un PowerPC o un'altra architettura big-endian, queste funzioni non scambieranno affatto i byte perché sono già nell'ordine dei byte di rete.
Frosty,

2
Sfortunatamente, htonle ntohlnon posso andare a little endian su una piattaforma big-endian.
Brian Vandenberg,

2
@celtschk, capito; tuttavia, l'OP vuole un modo per cambiare endianness, anche in un ambiente big-endian.
Brian Vandenberg,

4
Per evitare l'inevitabile domanda: ci sono una serie di ragioni per avere bisogno di LE per una piattaforma BE; un certo numero di formati di file (bmp, fli, pcx, qtm, rtf, tga per citarne alcuni) usano piccoli valori endian ... o almeno, qualche versione del formato lo ha fatto in una volta comunque.
Brian Vandenberg,

26

Ho preso alcuni suggerimenti da questo post e li ho messi insieme per formare questo:

#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>

enum endianness
{
    little_endian,
    big_endian,
    network_endian = big_endian,

    #if defined(BOOST_LITTLE_ENDIAN)
        host_endian = little_endian
    #elif defined(BOOST_BIG_ENDIAN)
        host_endian = big_endian
    #else
        #error "unable to determine system endianness"
    #endif
};

namespace detail {

template<typename T, size_t sz>
struct swap_bytes
{
    inline T operator()(T val)
    {
        throw std::out_of_range("data size");
    }
};

template<typename T>
struct swap_bytes<T, 1>
{
    inline T operator()(T val)
    {
        return val;
    }
};

template<typename T>
struct swap_bytes<T, 2>
{
    inline T operator()(T val)
    {
        return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
    }
};

template<typename T>
struct swap_bytes<T, 4>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff000000) >> 24) |
                (((val) & 0x00ff0000) >>  8) |
                (((val) & 0x0000ff00) <<  8) |
                (((val) & 0x000000ff) << 24));
    }
};

template<>
struct swap_bytes<float, 4>
{
    inline float operator()(float val)
    {
        uint32_t mem =swap_bytes<uint32_t, sizeof(uint32_t)>()(*(uint32_t*)&val);
        return *(float*)&mem;
    }
};

template<typename T>
struct swap_bytes<T, 8>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff00000000000000ull) >> 56) |
                (((val) & 0x00ff000000000000ull) >> 40) |
                (((val) & 0x0000ff0000000000ull) >> 24) |
                (((val) & 0x000000ff00000000ull) >> 8 ) |
                (((val) & 0x00000000ff000000ull) << 8 ) |
                (((val) & 0x0000000000ff0000ull) << 24) |
                (((val) & 0x000000000000ff00ull) << 40) |
                (((val) & 0x00000000000000ffull) << 56));
    }
};

template<>
struct swap_bytes<double, 8>
{
    inline double operator()(double val)
    {
        uint64_t mem =swap_bytes<uint64_t, sizeof(uint64_t)>()(*(uint64_t*)&val);
        return *(double*)&mem;
    }
};

template<endianness from, endianness to, class T>
struct do_byte_swap
{
    inline T operator()(T value)
    {
        return swap_bytes<T, sizeof(T)>()(value);
    }
};
// specialisations when attempting to swap to the same endianess
template<class T> struct do_byte_swap<little_endian, little_endian, T> { inline T operator()(T value) { return value; } };
template<class T> struct do_byte_swap<big_endian,    big_endian,    T> { inline T operator()(T value) { return value; } };

} // namespace detail

template<endianness from, endianness to, class T>
inline T byte_swap(T value)
{
    // ensure the data is only 1, 2, 4 or 8 bytes
    BOOST_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
    // ensure we're only swapping arithmetic types
    BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

    return detail::do_byte_swap<from, to, T>()(value);
}

devi anche includere <cstdint> o <stdint.h>, ad esempio, per uint32_t
ady

17

La procedura per passare da big-endian a little-endian è la stessa di passare da little-endian a big-endian.

Ecco qualche esempio di codice:

void swapByteOrder(unsigned short& us)
{
    us = (us >> 8) |
         (us << 8);
}

void swapByteOrder(unsigned int& ui)
{
    ui = (ui >> 24) |
         ((ui<<8) & 0x00FF0000) |
         ((ui>>8) & 0x0000FF00) |
         (ui << 24);
}

void swapByteOrder(unsigned long long& ull)
{
    ull = (ull >> 56) |
          ((ull<<40) & 0x00FF000000000000) |
          ((ull<<24) & 0x0000FF0000000000) |
          ((ull<<8) & 0x000000FF00000000) |
          ((ull>>8) & 0x00000000FF000000) |
          ((ull>>24) & 0x0000000000FF0000) |
          ((ull>>40) & 0x000000000000FF00) |
          (ull << 56);
}

2
L'ultima funzione pubblicata qui non è corretta e deve essere modificata in: void swapByteOrder (unsigned long long & ull) {ull = (ull >> 56) | ... (ull << 56); }
Eric Burnett,

14
Non penso che sia corretto usare la logica-e (&&) al contrario di bitwise-e (&). Secondo le specifiche C ++, entrambi gli operandi vengono convertiti implicitamente in bool, che non è quello che si desidera.
Trevor Robinson,

16

C'è un'istruzione di assemblaggio chiamata BSWAP che farà lo scambio per te, estremamente veloce . Puoi leggerlo qui .

Visual Studio, o più precisamente la libreria di runtime di Visual C ++, ha intrinsechi di piattaforma per questo, chiamati _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Simile dovrebbe esistere per altre piattaforme, ma non sono consapevole di come si chiamerebbero.


Questo è un ottimo collegamento. Ha riacceso il mio interesse per l'assemblatore x86.
PP.

1
I risultati dei tempi per BSWAP sono presentati qui. gmplib.org/~tege/x86-timing.pdf ... e qui ... agner.org/optimize/instruction_tables.pdf

12

Lo abbiamo fatto con i modelli. Potresti fare qualcosa del genere:

// Specialization for 2-byte types.
template<>
inline void endian_byte_swapper< 2 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    ushort* p_dest = reinterpret_cast< ushort* >(dest);
    ushort const* const p_src = reinterpret_cast< ushort const* >(src);
    *p_dest = (*p_src >> 8) | (*p_src << 8);
}

// Specialization for 4-byte types.
template<>
inline void endian_byte_swapper< 4 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    uint* p_dest = reinterpret_cast< uint* >(dest);
    uint const* const p_src = reinterpret_cast< uint const* >(src);
    *p_dest = (*p_src >> 24) | ((*p_src & 0x00ff0000) >> 8) | ((*p_src & 0x0000ff00) << 8) | (*p_src << 24);
}

8

Se lo stai facendo per trasferire dati tra piattaforme diverse, guarda le funzioni ntoh e hton.


7

Allo stesso modo in cui fai in C:

short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));

Potresti anche dichiarare un vettore di caratteri senza segno, memcpy il valore di input in esso, invertire i byte in un altro vettore e memcpy i byte fuori, ma ciò richiederà ordini di grandezza più lunghi del bit-twiddling, specialmente con valori a 64 bit.


7

Sulla maggior parte dei sistemi POSIX (attraverso non è nello standard POSIX) c'è endian.h, che può essere usato per determinare quale codifica usa il tuo sistema. Da lì c'è qualcosa del genere:

unsigned int change_endian(unsigned int x)
{
    unsigned char *ptr = (unsigned char *)&x;
    return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
}

Questo scambia l'ordine (da big endian a little endian):

Se hai il numero 0xDEADBEEF (su un piccolo sistema endian memorizzato come 0xEFBEADDE), ptr [0] sarà 0xEF, ptr [1] è 0xBE, ecc.

Ma se vuoi usarlo per il networking, allora htons, htonl e htonll (e le loro inverse ntohs, ntohl e ntohll) saranno utili per la conversione da ordine host a ordine di rete.


6
È divertente - lo standard POSIX su opengroup.org/onlinepubs/9699919799/toc.htm non menziona un'intestazione '<endian.h> `.
Jonathan Leffler,

1
È possibile utilizzare htonle gli amici indipendentemente dal fatto che il caso d'uso abbia a che fare con la rete. L'ordine dei byte di rete è big-endian, quindi considera queste funzioni come host_to_be e be_to_host. (Non aiuta se hai bisogno di host_to_le, però.)
Peter Cordes,

5

Si noti che, almeno per Windows, htonl () è molto più lento della loro controparte intrinseca _byteswap_ulong (). La prima è una chiamata alla libreria DLL in ws2_32.dll, la seconda è un'istruzione di assemblaggio BSWAP. Pertanto, se stai scrivendo un codice dipendente dalla piattaforma, preferisci usare gli intrinseci per la velocità:

#define htonl(x) _byteswap_ulong(x)

Ciò può essere particolarmente importante per l'elaborazione delle immagini .PNG in cui tutti i numeri interi vengono salvati in Big Endian con la spiegazione "Uno può usare htonl () ..." {per rallentare i tipici programmi Windows, se non si è preparati}.


4

La maggior parte delle piattaforme ha un file di intestazione del sistema che fornisce efficienti funzioni byteswap. Su Linux è dentro <endian.h>. Puoi avvolgerlo bene in C ++:

#include <iostream>

#include <endian.h>

template<size_t N> struct SizeT {};

#define BYTESWAPS(bits) \
template<class T> inline T htobe(T t, SizeT<bits / 8>) { return htobe ## bits(t); } \
template<class T> inline T htole(T t, SizeT<bits / 8>) { return htole ## bits(t); } \
template<class T> inline T betoh(T t, SizeT<bits / 8>) { return be ## bits ## toh(t); } \
template<class T> inline T letoh(T t, SizeT<bits / 8>) { return le ## bits ## toh(t); }

BYTESWAPS(16)
BYTESWAPS(32)
BYTESWAPS(64)

#undef BYTESWAPS

template<class T> inline T htobe(T t) { return htobe(t, SizeT<sizeof t>()); }
template<class T> inline T htole(T t) { return htole(t, SizeT<sizeof t>()); }
template<class T> inline T betoh(T t) { return betoh(t, SizeT<sizeof t>()); }
template<class T> inline T letoh(T t) { return letoh(t, SizeT<sizeof t>()); }

int main()
{
    std::cout << std::hex;
    std::cout << htobe(static_cast<unsigned short>(0xfeca)) << '\n';
    std::cout << htobe(0xafbeadde) << '\n';

    // Use ULL suffix to specify integer constant as unsigned long long 
    std::cout << htobe(0xfecaefbeafdeedfeULL) << '\n';
}

Produzione:

cafe
deadbeaf
feeddeafbeefcafe

Modifica: #define BYTESWAPS (bit) \ template <classe T> in linea T htobe (T t, SizeT <bit / 8>) {return htobe ## bit (t); } \ template <class T> inline T htole (T t, SizeT <bits / 8>) {return htole ## bits (t); } \ template <class T> inline T betoh (T t, SizeT <bits / 8>) {return be ## bits ## toh (t); } \ template <class T> inline T letoh (T t, SizeT <bits / 8>) {return le ## bits ## toh (t); }
ldav1s,

Grazie, ho dimenticato di testare betoh () e letoh ().
Maxim Egorushkin,

4

mi piace questo, solo per stile :-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}

Ottengo un errore char[]dicendo 'Errore: tipo incompleto non è consentito'
Portland Runner

4

Seriamente ... Non capisco perché tutte le soluzioni siano così complicate ! Che ne dite della funzione modello più semplice e più generale che scambia qualsiasi tipo di qualsiasi dimensione in qualsiasi circostanza in qualsiasi sistema operativo ????

template <typename T>
void SwapEnd(T& var)
{
    static_assert(std::is_pod<T>::value, "Type must be POD type for safety");
    std::array<char, sizeof(T)> varArray;
    std::memcpy(varArray.data(), &var, sizeof(T));
    for(int i = 0; i < static_cast<int>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
    std::memcpy(&var, varArray.data(), sizeof(T));
}

È il potere magico di C e C ++ insieme! Basta scambiare la variabile originale carattere per carattere.

Punto 1 : Nessun operatore: ricorda che non ho usato l'operatore di assegnazione semplice "=" perché alcuni oggetti verranno incasinati quando l'endianness viene capovolto e il costruttore di copie (o operatore di assegnazione) non funzionerà. Pertanto, è più affidabile copiarli char con char.

Punto 2 : attenzione ai problemi di allineamento: notare che stiamo copiando da e verso un array, che è la cosa giusta da fare perché il compilatore C ++ non garantisce che possiamo accedere alla memoria non allineata (questa risposta è stata aggiornata dalla sua versione originale modulo per questo). Ad esempio, se si assegna uint64_t, il compilatore non può garantire di poter accedere al terzo byte di quello come a uint8_t. Pertanto, la cosa giusta da fare è copiare questo in un array di caratteri, scambiarlo, quindi copiarlo nuovamente (quindi no reinterpret_cast). Nota che i compilatori sono per lo più abbastanza intelligenti da convertire ciò che hai fatto in reinterpret_castse sono in grado di accedere a singoli byte indipendentemente dall'allineamento.

Per utilizzare questa funzione :

double x = 5;
SwapEnd(x);

e ora xè diverso in endianness.


2
Ciò funzionerà ovunque, ma il montaggio OCDE prodotto sarà spesso non ottimale: vedi la mia domanda stackoverflow.com/questions/36657895/...
j_kubik

Si utilizza new/ deleteper allocare un buffer per questo?!? sizeof(var)è una costante di compilazione, quindi potresti farlo char varSwapped[sizeof(var)]. Oppure potresti fare char *p = reinterpret_cast<char*>(&var)e scambiare sul posto.
Peter Cordes,

@Peter questa risposta è veloce e sporca fatta per dimostrare un punto. Implementerò i tuoi suggerimenti. Tuttavia, non devi essere un mega SO AH e sottovalutare la soluzione a 5 righe rispetto alle soluzioni a 50 righe che vengono fornite lì. Non dirò di più.
Il fisico quantistico,

Questa risposta evidenzia alcuni punti utili sull'attenzione con costruttori e operatori sovraccarichi su dati di tipo end-end, quindi sarei felice di rimuovere il mio downvote una volta che il codice non è orribile, ed è qualcosa che un buon compilatore potrebbe compilare in un bswap istruzioni. Inoltre, suggerirei di utilizzare for(size_t i = 0 ; i < sizeof(var) ; i++)invece di a static_cast<long>. (O in realtà, lo swap sul posto utilizzerà un ordine ascendente e discendente char*che scompare comunque).
Peter Cordes,

ad es. vedere la risposta di Mark Ransom usando std :: swap per invertire sul posto.
Peter Cordes,

3

Ho questo codice che mi consente di convertire da HOST_ENDIAN_ORDER (qualunque esso sia) a LITTLE_ENDIAN_ORDER o BIG_ENDIAN_ORDER. Uso un modello, quindi se provo a convertire da HOST_ENDIAN_ORDER a LITTLE_ENDIAN_ORDER e sono uguali per la macchina per la quale compilo, non verrà generato alcun codice.

Ecco il codice con alcuni commenti:

// We define some constant for little, big and host endianess. Here I use 
// BOOST_LITTLE_ENDIAN/BOOST_BIG_ENDIAN to check the host indianess. If you
// don't want to use boost you will have to modify this part a bit.
enum EEndian
{
  LITTLE_ENDIAN_ORDER,
  BIG_ENDIAN_ORDER,
#if defined(BOOST_LITTLE_ENDIAN)
  HOST_ENDIAN_ORDER = LITTLE_ENDIAN_ORDER
#elif defined(BOOST_BIG_ENDIAN)
  HOST_ENDIAN_ORDER = BIG_ENDIAN_ORDER
#else
#error "Impossible de determiner l'indianness du systeme cible."
#endif
};

// this function swap the bytes of values given it's size as a template
// parameter (could sizeof be used?).
template <class T, unsigned int size>
inline T SwapBytes(T value)
{
  union
  {
     T value;
     char bytes[size];
  } in, out;

  in.value = value;

  for (unsigned int i = 0; i < size / 2; ++i)
  {
     out.bytes[i] = in.bytes[size - 1 - i];
     out.bytes[size - 1 - i] = in.bytes[i];
  }

  return out.value;
}

// Here is the function you will use. Again there is two compile-time assertion
// that use the boost librarie. You could probably comment them out, but if you
// do be cautious not to use this function for anything else than integers
// types. This function need to be calles like this :
//
//     int x = someValue;
//     int i = EndianSwapBytes<HOST_ENDIAN_ORDER, BIG_ENDIAN_ORDER>(x);
//
template<EEndian from, EEndian to, class T>
inline T EndianSwapBytes(T value)
{
  // A : La donnée à swapper à une taille de 2, 4 ou 8 octets
  BOOST_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);

  // A : La donnée à swapper est d'un type arithmetic
  BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

  // Si from et to sont du même type on ne swap pas.
  if (from == to)
     return value;

  return SwapBytes<T, sizeof(T)>(value);
}

3

Se un intero senza segno a 32 bit big-endian assomiglia a 0xAABBCCDD che è uguale a 2864434397, lo stesso intero senza segno a 32 bit sembra 0xDDCCBBAA su un processore little-endian che è anche uguale a 2864434397.

Se un short senza segno a 16 bit big-endian assomiglia a 0xAABB che è uguale a 43707, lo stesso short senza segno a 16 bit sembra 0xBBAA su un processore little-endian che è anche uguale a 43707.

Ecco un paio di utili funzioni #define per scambiare byte da little-endian a big-endian e viceversa ->

// can be used for short, unsigned short, word, unsigned word (2-byte types)
#define BYTESWAP16(n) (((n&0xFF00)>>8)|((n&0x00FF)<<8))

// can be used for int or unsigned int or float (4-byte types)
#define BYTESWAP32(n) ((BYTESWAP16((n&0xFFFF0000)>>16))|((BYTESWAP16(n&0x0000FFFF))<<16))

// can be used for unsigned long long or double (8-byte types)
#define BYTESWAP64(n) ((BYTESWAP32((n&0xFFFFFFFF00000000)>>32))|((BYTESWAP32(n&0x00000000FFFFFFFF))<<32))

2

Ecco una versione generalizzata che mi è venuta in mente, per scambiare un valore sul posto. Gli altri suggerimenti sarebbero migliori se le prestazioni sono un problema.

 template<typename T>
    void ByteSwap(T * p)
    {
        for (int i = 0;  i < sizeof(T)/2;  ++i)
            std::swap(((char *)p)[i], ((char *)p)[sizeof(T)-1-i]);
    }

Disclaimer: non ho provato a compilare questo o testarlo ancora.


2

Se prendi il modello comune per invertire l'ordine dei bit in una parola e elimini la parte che inverte i bit all'interno di ogni byte, allora ti rimane qualcosa che inverte solo i byte all'interno di una parola. Per 64 bit:

x = ((x & 0x00000000ffffffff) << 32) ^ ((x >> 32) & 0x00000000ffffffff);
x = ((x & 0x0000ffff0000ffff) << 16) ^ ((x >> 16) & 0x0000ffff0000ffff);
x = ((x & 0x00ff00ff00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff00ff00ff);

Il compilatore dovrebbe ripulire le operazioni di mascheramento dei bit superflue (le ho lasciate per evidenziare il modello), ma in caso contrario è possibile riscrivere la prima riga in questo modo:

x = ( x                       << 32) ^  (x >> 32);

Ciò dovrebbe normalmente semplificare fino a una singola istruzione di rotazione sulla maggior parte delle architetture (ignorando che l'intera operazione è probabilmente un'istruzione).

Su un processore RISC le costanti grandi e complicate possono causare difficoltà al compilatore. Tuttavia, è possibile calcolare banalmente ciascuna delle costanti dalla precedente. Così:

uint64_t k = 0x00000000ffffffff; /* compiler should know a trick for this */
x = ((x & k) << 32) ^ ((x >> 32) & k);
k ^= k << 16;
x = ((x & k) << 16) ^ ((x >> 16) & k);
k ^= k << 8;
x = ((x & k) <<  8) ^ ((x >>  8) & k);

Se vuoi, puoi scriverlo come un ciclo. Non sarà efficiente, ma solo per divertimento:

int i = sizeof(x) * CHAR_BIT / 2;
uintmax_t k = (1 << i) - 1;
while (i >= 8)
{
    x = ((x & k) << i) ^ ((x >> i) & k);
    i >>= 1;
    k ^= k << i;
}

E per completezza, ecco la versione semplificata a 32 bit del primo modulo:

x = ( x               << 16) ^  (x >> 16);
x = ((x & 0x00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff);

2

Ho pensato di aver aggiunto la mia soluzione qui poiché non l'ho vista da nessuna parte. È una funzione modellata C ++ piccola e portatile e portatile che utilizza solo operazioni a bit.

template<typename T> inline static T swapByteOrder(const T& val) {
    int totalBytes = sizeof(val);
    T swapped = (T) 0;
    for (int i = 0; i < totalBytes; ++i) {
        swapped |= (val >> (8*(totalBytes-i-1)) & 0xFF) << (8*i);
    }
    return swapped;
}

2

Sono davvero sorpreso che nessuno abbia menzionato le funzioni htobeXX e betohXX. Sono definiti in endian.h e sono molto simili alle funzioni di rete htonXX.


2

Utilizzando i codici seguenti, è possibile passare facilmente da BigEndian a LittleEndian

#define uint32_t unsigned 
#define uint16_t unsigned short

#define swap16(x) ((((uint16_t)(x) & 0x00ff)<<8)| \
(((uint16_t)(x) & 0xff00)>>8))

#define swap32(x) ((((uint32_t)(x) & 0x000000ff)<<24)| \
(((uint32_t)(x) & 0x0000ff00)<<8)| \
(((uint32_t)(x) & 0x00ff0000)>>8)| \
(((uint32_t)(x) & 0xff000000)>>24))

1

Di recente ho scritto una macro per farlo in C, ma è ugualmente valido in C ++:

#define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES<sizeof(__VA_ARGS__)>>1; ++REVERSE_BYTES)\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
while(0)

Accetta qualsiasi tipo e inverte i byte nell'argomento passato. Esempi di utilizzo:

int main(){
    unsigned long long x = 0xABCDEF0123456789;
    printf("Before: %llX\n",x);
    REVERSE_BYTES(x);
    printf("After : %llX\n",x);

    char c[7]="nametag";
    printf("Before: %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
    REVERSE_BYTES(c);
    printf("After : %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
}

Che stampa:

Before: ABCDEF0123456789
After : 8967452301EFCDAB
Before: nametag
After : gateman

Quanto sopra è perfettamente copiabile / incollabile, ma c'è molto da fare qui, quindi analizzerò come funziona pezzo per pezzo:

La prima cosa degna di nota è che l'intera macro è racchiusa in un do while(0)blocco. Questo è un linguaggio comune per consentire il normale uso del punto e virgola dopo la macro.

Il prossimo è l'uso di una variabile chiamata REVERSE_BYTEScome forcontatore del loop. Il nome della macro stessa viene utilizzato come nome di una variabile per garantire che non sia in conflitto con altri simboli che potrebbero trovarsi nell'ambito nell'ambito della macro. Poiché il nome viene utilizzato all'interno dell'espansione della macro, non verrà più espanso se utilizzato come nome di variabile qui.

All'interno del forciclo, ci sono due byte a cui viene fatto riferimento e scambiati XOR (quindi non è richiesto un nome di variabile temporaneo):

((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]

__VA_ARGS__rappresenta tutto ciò che è stato dato alla macro e viene utilizzato per aumentare la flessibilità di ciò che può essere passato (anche se non di molto). L'indirizzo di questo argomento viene quindi preso e trasmesso a un unsigned charpuntatore per consentire lo scambio dei suoi byte tramite array[] sottoscrizione di .

L'ultimo punto peculiare è la mancanza di {}parentesi graffe. Non sono necessari perché tutti i passaggi di ogni scambio sono uniti con l' operatore virgola , rendendoli un'istruzione.

Infine, vale la pena notare che questo non è l'approccio ideale se la velocità è una priorità assoluta. Se questo è un fattore importante, alcune delle macro specifiche del tipo o delle direttive specifiche della piattaforma citate in altre risposte sono probabilmente un'opzione migliore. Questo approccio, tuttavia, è portatile per tutti i tipi, tutte le principali piattaforme e entrambi i linguaggi C e C ++.


trovato questo da qualche parte in qualche codice. confuso il diavolo fuori di me. Grazie per la spiegazione. Tuttavia, perché l'uso di __VA_ARGS__?
asr9,

0

Wow, non potevo credere ad alcune delle risposte che ho letto qui. In realtà c'è un'istruzione in assemblea che lo fa più velocemente di ogni altra cosa. bswap. Potresti semplicemente scrivere una funzione come questa ...

__declspec(naked) uint32_t EndianSwap(uint32 value)
{
    __asm
    {
        mov eax, dword ptr[esp + 4]
        bswap eax
        ret
    }
}

È MOLTO più veloce degli intrinseci che sono stati suggeriti. Li ho smontati e guardato. La funzione precedente non ha prologo / epilogo, quindi praticamente non ha alcun sovraccarico.

unsigned long _byteswap_ulong(unsigned long value);

Fare 16 bit è altrettanto facile, con l'eccezione che avresti usato xchg al, ah. bswap funziona solo su registri a 32 bit.

64 bit è un po 'più complicato, ma non eccessivamente. Molto meglio di tutti gli esempi sopra con loop e template ecc.

Ci sono alcuni avvertimenti qui ... Innanzitutto bswap è disponibile solo su CPU 80x486 e successive. Qualcuno ha intenzione di eseguirlo su un 386?!? In tal caso, puoi comunque sostituire bswap con ...

mov ebx, eax
shr ebx, 16
xchg bl, bh
xchg al, ah
shl eax, 16
or eax, ebx

Anche l'assemblaggio in linea è disponibile solo nel codice x86 in Visual Studio. Una funzione nuda non può essere allineata e inoltre non è disponibile nelle build x64. In quell'istanza, dovrai usare i intrinseci del compilatore.


1
_byteswap_ulonge _uint64(ad es. nella risposta accettata) compilano entrambi per utilizzare l' bswapistruzione. Sarei sorpreso ma interessato a sapere se questo asm è molto più veloce in quanto omette solo il prologo / epilogo - lo hai valutato?
ZachB,

@stdcall La domanda non ha richiesto una soluzione portatile né menzionato nulla su una piattaforma. Come ho detto la mia risposta, quanto sopra è circa il modo più veloce di scambiare endian. Certo, se stai scrivendo questo su una piattaforma non X86, allora questo non funzionerà, ma come ho anche detto, allora sei limitato agli intrinseci del compilatore, se il tuo compilatore li supporta anche.
The Welder,

@ZachB In questo caso particolare, penso che omettere il prologo e l'epilogo ti darà un risparmio decente perché essenzialmente stai eseguendo solo 1 istruzione. Il prologo dovrà spingere nello stack, fare una sottrazione, impostare il puntatore di base e quindi simile alla fine. Non l'ho confrontato, ma quanto sopra ha una catena di dipendenze 0 che semplicemente non otterrai senza che sia nuda. Forse un buon compilatore lo incorporerebbe, ma poi ti trovi in ​​un parco giochi diverso.
The Welder,

2
Forse. Ma nota che nel caso comune di scambio di una matrice di numeri, i valori intrinseci del compilatore discussi in altre risposte useranno estensioni SSE / AVX ed emetteranno PSHUFB, che supera le prestazioni di BSWAP. Vedi wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB

È una cattiva idea da parte di IMHO pubblicare una soluzione specifica per piattaforma, quando l'OP non ha specificato che avevano solo bisogno di una soluzione per x86. E per denigrare le altre soluzioni, quando la tua è inutilizzabile su molti sistemi operativi molto usati come iOS e Android (che usano CPU ARM o MIPS).
Jens Alfke,

0

Tecnica portatile per l'implementazione di accessori endian non inplace non allineati e ottimizzatori. Funzionano su ogni compilatore, ogni allineamento dei confini e ogni ordinamento di byte. Queste routine non allineate vengono integrate o modificate, a seconda dell'endiano e dell'allineamento nativi. Elenco parziale ma hai avuto l'idea. BO * sono valori costanti basati sull'ordinamento di byte nativo.

uint32_t sw_get_uint32_1234(pu32)
uint32_1234 *pu32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32_1234[0] = (*pu32)[BO32_0];
  bou32.u32_1234[1] = (*pu32)[BO32_1];
  bou32.u32_1234[2] = (*pu32)[BO32_2];
  bou32.u32_1234[3] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_1234(pu32, u32)
uint32_1234 *pu32;
uint32_t u32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_1234[0];
  (*pu32)[BO32_1] = bou32.u32_1234[1];
  (*pu32)[BO32_2] = bou32.u32_1234[2];
  (*pu32)[BO32_3] = bou32.u32_1234[3];
}

#if HAS_SW_INT64
int64 sw_get_int64_12345678(pi64)
int64_12345678 *pi64;
{
  union {
    int64_12345678 i64_12345678;
    int64 i64;
  } boi64;
  boi64.i64_12345678[0] = (*pi64)[BO64_0];
  boi64.i64_12345678[1] = (*pi64)[BO64_1];
  boi64.i64_12345678[2] = (*pi64)[BO64_2];
  boi64.i64_12345678[3] = (*pi64)[BO64_3];
  boi64.i64_12345678[4] = (*pi64)[BO64_4];
  boi64.i64_12345678[5] = (*pi64)[BO64_5];
  boi64.i64_12345678[6] = (*pi64)[BO64_6];
  boi64.i64_12345678[7] = (*pi64)[BO64_7];
  return(boi64.i64);
}
#endif

int32_t sw_get_int32_3412(pi32)
int32_3412 *pi32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32_3412[2] = (*pi32)[BO32_0];
  boi32.i32_3412[3] = (*pi32)[BO32_1];
  boi32.i32_3412[0] = (*pi32)[BO32_2];
  boi32.i32_3412[1] = (*pi32)[BO32_3];
  return(boi32.i32);
}

void sw_set_int32_3412(pi32, i32)
int32_3412 *pi32;
int32_t i32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32 = i32;
  (*pi32)[BO32_0] = boi32.i32_3412[2];
  (*pi32)[BO32_1] = boi32.i32_3412[3];
  (*pi32)[BO32_2] = boi32.i32_3412[0];
  (*pi32)[BO32_3] = boi32.i32_3412[1];
}

uint32_t sw_get_uint32_3412(pu32)
uint32_3412 *pu32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32_3412[2] = (*pu32)[BO32_0];
  bou32.u32_3412[3] = (*pu32)[BO32_1];
  bou32.u32_3412[0] = (*pu32)[BO32_2];
  bou32.u32_3412[1] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_3412(pu32, u32)
uint32_3412 *pu32;
uint32_t u32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_3412[2];
  (*pu32)[BO32_1] = bou32.u32_3412[3];
  (*pu32)[BO32_2] = bou32.u32_3412[0];
  (*pu32)[BO32_3] = bou32.u32_3412[1];
}

float sw_get_float_1234(pf)
float_1234 *pf;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f_1234[0] = (*pf)[BO32_0];
  bof.f_1234[1] = (*pf)[BO32_1];
  bof.f_1234[2] = (*pf)[BO32_2];
  bof.f_1234[3] = (*pf)[BO32_3];
  return(bof.f);
}

void sw_set_float_1234(pf, f)
float_1234 *pf;
float f;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f = (float)f;
  (*pf)[BO32_0] = bof.f_1234[0];
  (*pf)[BO32_1] = bof.f_1234[1];
  (*pf)[BO32_2] = bof.f_1234[2];
  (*pf)[BO32_3] = bof.f_1234[3];
}

double sw_get_double_12345678(pd)
double_12345678 *pd;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d_12345678[0] = (*pd)[BO64_0];
  bod.d_12345678[1] = (*pd)[BO64_1];
  bod.d_12345678[2] = (*pd)[BO64_2];
  bod.d_12345678[3] = (*pd)[BO64_3];
  bod.d_12345678[4] = (*pd)[BO64_4];
  bod.d_12345678[5] = (*pd)[BO64_5];
  bod.d_12345678[6] = (*pd)[BO64_6];
  bod.d_12345678[7] = (*pd)[BO64_7];
  return(bod.d);
}

void sw_set_double_12345678(pd, d)
double_12345678 *pd;
double d;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d = d;
  (*pd)[BO64_0] = bod.d_12345678[0];
  (*pd)[BO64_1] = bod.d_12345678[1];
  (*pd)[BO64_2] = bod.d_12345678[2];
  (*pd)[BO64_3] = bod.d_12345678[3];
  (*pd)[BO64_4] = bod.d_12345678[4];
  (*pd)[BO64_5] = bod.d_12345678[5];
  (*pd)[BO64_6] = bod.d_12345678[6];
  (*pd)[BO64_7] = bod.d_12345678[7];
}

Questi typedef hanno il vantaggio di aumentare gli errori del compilatore se non utilizzati con gli accessor, mitigando così i bug dimenticati degli accessor.

typedef char int8_1[1], uint8_1[1];

typedef char int16_12[2], uint16_12[2]; /* little endian */
typedef char int16_21[2], uint16_21[2]; /* big endian */

typedef char int24_321[3], uint24_321[3]; /* Alpha Micro, PDP-11 */

typedef char int32_1234[4], uint32_1234[4]; /* little endian */
typedef char int32_3412[4], uint32_3412[4]; /* Alpha Micro, PDP-11 */
typedef char int32_4321[4], uint32_4321[4]; /* big endian */

typedef char int64_12345678[8], uint64_12345678[8]; /* little endian */
typedef char int64_34128756[8], uint64_34128756[8]; /* Alpha Micro, PDP-11 */
typedef char int64_87654321[8], uint64_87654321[8]; /* big endian */

typedef char float_1234[4]; /* little endian */
typedef char float_3412[4]; /* Alpha Micro, PDP-11 */
typedef char float_4321[4]; /* big endian */

typedef char double_12345678[8]; /* little endian */
typedef char double_78563412[8]; /* Alpha Micro? */
typedef char double_87654321[8]; /* big endian */

2
Per questa domanda, il tag C ++ fa la differenza. Esistono molti comportamenti indefiniti dovuti al C ++ e al sindacato.
jww

0

Ecco come leggere un doppio archiviato in formato IEEE 754 a 64 bit, anche se il computer host utilizza un sistema diverso.

/*
* read a double from a stream in ieee754 format regardless of host
*  encoding.
*  fp - the stream
*  bigendian - set to if big bytes first, clear for little bytes
*              first
*
*/
double freadieee754(FILE *fp, int bigendian)
{
    unsigned char buff[8];
    int i;
    double fnorm = 0.0;
    unsigned char temp;
    int sign;
    int exponent;
    double bitval;
    int maski, mask;
    int expbits = 11;
    int significandbits = 52;
    int shift;
    double answer;

    /* read the data */
    for (i = 0; i < 8; i++)
        buff[i] = fgetc(fp);
    /* just reverse if not big-endian*/
    if (!bigendian)
    {
        for (i = 0; i < 4; i++)
        {
            temp = buff[i];
            buff[i] = buff[8 - i - 1];
            buff[8 - i - 1] = temp;
        }
    }
    sign = buff[0] & 0x80 ? -1 : 1;
    /* exponet in raw format*/
    exponent = ((buff[0] & 0x7F) << 4) | ((buff[1] & 0xF0) >> 4);

    /* read inthe mantissa. Top bit is 0.5, the successive bits half*/
    bitval = 0.5;
    maski = 1;
    mask = 0x08;
    for (i = 0; i < significandbits; i++)
    {
        if (buff[maski] & mask)
            fnorm += bitval;

        bitval /= 2.0;
        mask >>= 1;
        if (mask == 0)
        {
            mask = 0x80;
            maski++;
        }
    }
    /* handle zero specially */
    if (exponent == 0 && fnorm == 0)
        return 0.0;

    shift = exponent - ((1 << (expbits - 1)) - 1); /* exponent = shift + bias */
    /* nans have exp 1024 and non-zero mantissa */
    if (shift == 1024 && fnorm != 0)
        return sqrt(-1.0);
    /*infinity*/
    if (shift == 1024 && fnorm == 0)
    {

#ifdef INFINITY
        return sign == 1 ? INFINITY : -INFINITY;
#endif
        return  (sign * 1.0) / 0.0;
    }
    if (shift > -1023)
    {
        answer = ldexp(fnorm + 1.0, shift);
        return answer * sign;
    }
    else
    {
        /* denormalised numbers */
        if (fnorm == 0.0)
            return 0.0;
        shift = -1022;
        while (fnorm < 1.0)
        {
            fnorm *= 2;
            shift--;
        }
        answer = ldexp(fnorm, shift);
        return answer * sign;
    }
}

Per il resto della suite di funzioni, comprese le routine di scrittura e numero intero, vedi il mio progetto github

https://github.com/MalcolmMcLean/ieee754


0

Lo scambio di byte con il vecchio trucco in 3 passaggi xor attorno a un perno in una funzione modello fornisce una soluzione O (ln2) flessibile e rapida che non richiede una libreria, lo stile qui rifiuta anche i tipi di 1 byte:

template<typename T>void swap(T &t){
    for(uint8_t pivot = 0; pivot < sizeof(t)/2; pivot ++){
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
        *((uint8_t *)&t+sizeof(t)-1- pivot) ^= *((uint8_t *)&t + pivot);
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
    }
}

0

Sembra che il modo sicuro sarebbe usare hton su ogni parola. Quindi, se hai ...

std::vector<uint16_t> storage(n);  // where n is the number to be converted

// the following would do the trick
std::transform(word_storage.cbegin(), word_storage.cend()
  , word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });

Quanto sopra sarebbe un no-op se fossi su un sistema big-endian, quindi cercherei qualunque cosa la tua piattaforma usi come condizione di compilazione per decidere se htons è un no-op. Dopotutto è O (n). Su un Mac, sarebbe qualcosa come ...

#if (__DARWIN_BYTE_ORDER != __DARWIN_BIG_ENDIAN)
std::transform(word_storage.cbegin(), word_storage.cend()
  , word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });
#endif

0

Se hai C ++ 17, aggiungi questa intestazione

#include <algorithm>

Utilizzare questa funzione modello per scambiare i byte:

template <typename T>
void swapEndian(T& buffer)
{
    static_assert(std::is_pod<T>::value, "swapEndian support POD type only");
    char* startIndex = static_cast<char*>((void*)buffer.data());
    char* endIndex = startIndex + sizeof(buffer);
    std::reverse(startIndex, endIndex);
}

chiamalo come:

swapEndian (stlContainer);

-4

Cerca un po 'di spostamento, dato che questo è fondamentalmente tutto ciò che devi fare per passare da little -> big endian. Quindi, a seconda della dimensione del bit, si cambia il modo in cui si sposta il bit.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.