Domanda

Come posso 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.

È stato utile?

Soluzione

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 numeri interi semplici che 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 intrinseche simili.

In GCC ad esempio puoi chiamare direttamente:

int32_t __builtin_bswap32 (int32_t x)
int64_t __builtin_bswap64 (int64_t x)

(non è necessario 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 propri ti dà le migliori prestazioni e densità di codice tra ..

Altri suggerimenti

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).

Da Fallimento dell'ordine byte di Rob Pyke:

  

Supponiamo che il tuo flusso di dati abbia un 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 data fosse un array di unsigned char o uint8_t. L'uso di signed char o char (se firmato) comporterà la promozione di data[x] in un numero intero e data[x] << 24 potenzialmente lo spostamento di un 1 nel bit di segno che è UB.

Se lo stai facendo ai fini della compatibilità di rete / host, dovresti 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.

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);
}

Esiste un'istruzione di assemblaggio chiamata BSWAP che eseguirà lo scambio per te, estremamente veloce . Puoi leggere qui .

Visual Studio, o più precisamente la libreria di runtime di Visual C ++, contiene elementi intrinseci della piattaforma, chiamati _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Simile dovrebbe esistere per altre piattaforme, ma non sono consapevole di come sarebbero chiamati.

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

Ecco un 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);
}

Lo abbiamo fatto con i modelli. Puoi così 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);
}

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

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 64- valori di bit.

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

unsigned int change_endian(unsinged 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 sistema little 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 i loro ntoh inversi, ntohl e ntohll) saranno utili per convertire da ordine host a ordine di rete.

Nota 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)

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

La maggior parte delle piattaforme ha un file di intestazione del sistema che fornisce efficienti funzioni di byteswap. Su Linux è in <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';
}

Output:

cafe
deadbeaf
feeddeafbeefcafe

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] };
}

Scherzi a parte ... Non capisco perché tutte le soluzioni siano complicate ! Che ne dici 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)
{
    char* varArray = reinterpret_cast<char*>(&var);
    for(long i = 0; i < static_cast<long>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
}

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

Ricorda che non ho usato l'operatore di assegnazione semplice " = " perché alcuni oggetti verranno incasinati quando viene capovolta l'endianness e il costruttore della copia (o operatore di assegnazione) non funzionerà. Pertanto, è più affidabile copiarli con caratteri char.

Per chiamarlo, basta usare

double x = 5;
SwapEnd(x);

e ora x è diverso per endianness.

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);
}

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 big-endian a 16 bit 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))

Ecco una versione generalizzata che mi è venuta in mente, per scambiare un valore sul posto. Gli altri suggerimenti sarebbero migliori se le prestazioni fossero 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 ancora provato a compilare questo o testarlo.

Se prendi il modello comune per invertire l'ordine dei bit in una parola e abbatti la parte che inverte i bit all'interno di ogni byte, allora rimani con 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 puoi 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. In questo modo:

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);

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;
}

Con i codici indicati di seguito puoi scambiare facilmente tra BigEndian e 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))

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.

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 elementi intrinseci che sono stati suggeriti. Li ho smontati e guardato. La funzione di cui sopra 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.

Tecnica portatile per l'implementazione di accessi endiani non allineati 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 errori di battitura hanno il vantaggio di aumentare gli errori del compilatore se non utilizzati con gli accessori, mitigando così i bug dimenticati degli accessori.

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 */

Di recente ho scritto una macro per farlo in C, ma è ugualmente valida 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]);
}

Quale 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 blocco do while(0). Questo è un idioma comune per consentire il normale uso del punto e virgola dopo il macro.

Il prossimo è l'uso di una variabile denominata REVERSE_BYTES come contatore del ciclo for. 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 ciclo __VA_ARGS__, ci sono due byte a cui viene fatto riferimento e XOR scambiati ( quindi non è richiesto un nome di variabile temporaneo):

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

unsigned char 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 [] puntatore per consentire lo scambio dei suoi byte tramite l'array {} sottoscrizione.

Il punto peculiare finale è la mancanza di <=> parentesi graffe. Non sono necessari perché tutti i passaggi di ogni scambio sono uniti con operatore virgola , rendendoli una dichiarazione.

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 ++.

Prova Boost::endian e NON IMPLEMENTARLO DA TE!

Ecco un link

Ecco come leggere un doppio archiviato in formato IEEE 754 a 64 bit, anche se il tuo 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 intera, vedere il mio progetto github

https://github.com/MalcolmMcLean/ieee754

Lo scambio di byte con il vecchio trucco in 3 passaggi xor un pivot 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);
    }
}

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 tu 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

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top