¿Cómo convierto entre valores big-endian y little-endian en C++?


¿Cómo convertir entre valores big-endian y little-endian en C++?

EDITAR: Para mayor claridad, tengo que traducir datos binarios (valores de coma flotante de doble precisión y enteros de 32 bits y 64 bits) de una arquitectura de CPU a otra. Esto no implica redes, por lo que ntoh() y funciones similares no funcionarán aquí.

EDIT #2: La respuesta que acepté se aplica directamente a los compiladores a los que estoy apuntando (por eso la elegí). Sin embargo, hay otros muy buenos, más portátiles respuestas aquí.

Author: Yu Hao, 2008-09-20

28 answers

Si está utilizando Visual C++ haga lo siguiente: Incluye intrin.h y llame a las siguientes funciones:

Para números de 16 bits:

unsigned short _byteswap_ushort(unsigned short value);

Para números de 32 bits:

unsigned long _byteswap_ulong(unsigned long value);

Para números de 64 bits:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

No es necesario convertir números de 8 bits (caracteres).

También estos solo se definen para valores sin signo que funcionan para enteros con signo también.

Para los flotadores y dobles es más difícil que con enteros simples, ya que estos pueden o no pueden estar en el orden de bytes de las máquinas host. Puedes obtener flotadores little-endian en máquinas big-endian y viceversa.

Otros compiladores también tienen intrínsecos similares.

En GCC por ejemplo, puede llamar directamente a:

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

(no hay necesidad de incluir algo). Afaik bits.h declara la misma función de una manera no centrada en gcc también.

16 bit swap es solo un bit-rotar.

Llamar a los intrínsecos en lugar de rodar los tuyos te da el mejor rendimiento y código densidad por cierto..

 140
Author: Nils Pipenbrinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 20:36:44

En pocas palabras:

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

Uso: swap_endian<uint32_t>(42).

 68
Author: Alexandre C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-06 19:55:55

Desde La Falacia Del Orden de Bytes por Rob Pyke:

Digamos que su flujo de datos tiene un entero de 32 bits codificado con un poco de endian. He aquí cómo extraerlo (asumiendo bytes sin signo):

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

Si es big-endian, he aquí cómo extraerlo:

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

TL; DR: no se preocupe por el orden nativo de su plataforma, todo lo que cuenta es el orden de bytes de la transmisión desde la que está leyendo, y espero que esté bien definido.

Nota: se comentó en el comentario que, a falta de conversión explícita de tipos, era importante que data fuera un array de unsigned char o uint8_t. Usar signed char o char (si está firmado) resultará en que data[x] se promueva a un entero y data[x] << 24 potencialmente cambie un 1 al bit de signo que es UB.

 58
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-10 19:53:52

Si está haciendo esto con fines de compatabilidad de red/host, debe usar:

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)

Si está haciendo esto por alguna otra razón, una de las soluciones byte_swap presentadas aquí funcionaría bien.

 47
Author: Frosty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 20:38:18

Tomé algunas sugerencias de este post y las junté para formar esto:

#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);
}
 25
Author: Steve Lorimer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-19 14:36:13

Hay una instrucción de ensamblaje llamada BSWAP que hará el intercambio por usted, extremadamente rápido. Puedes leer sobre ello aquí.

Visual Studio, o más precisamente la biblioteca de tiempo de ejecución de Visual C++, tiene intrínsecos de plataforma para esto, llamados _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Similar debería existir para otras plataformas, pero no soy consciente de cómo se llamarían.

 15
Author: anon6439,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 20:34:46

El procedimiento para pasar de big-endian a little-endian es el mismo que pasar de little-endian a big-endian.

Aquí hay un código de ejemplo:

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);
}
 12
Author: Kevin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-11 04:27:38

Hemos hecho esto con plantillas. Usted podría así algo como esto:

// 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);
}
 11
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 20:29:23

Si está haciendo esto para transferir datos entre diferentes plataformas, mire las funciones ntoh y hton.

 8
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 20:26:48

De la misma manera que lo haces en C:

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

También podría declarar un vector de caracteres sin signo, memcpy el valor de entrada en él, invertir los bytes en otro vector y memcpy los bytes hacia fuera, pero eso tomará órdenes de magnitud más largo que bit-twiddling, especialmente con valores de 64 bits.

 6
Author: Ben Straub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 20:30:07

En la mayoría de los sistemas POSIX (a través de que no está en el estándar POSIX) está el endian.h, que se puede utilizar para determinar qué codificación utiliza su sistema. A partir de ahí es algo como esto:

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

Esto cambia el orden (de big-endian a little endian):

Si tiene el número 0xDEADBEEF (en un pequeño sistema endian almacenado como 0xEFBEADDE), ptr[0] será 0xEF, ptr[1] es 0xBE, etc.

Pero si quieres usarlo para redes, entonces htons, htonl y htonll (y su inverse ntohs, ntohl y ntohll) será útil para convertir de orden de host a orden de red.

 6
Author: terminus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-01 17:30:23

Tenga en cuenta que, al menos para Windows, htonl() es mucho más lento que su contraparte intrínseca _byteswap_ulong(). El primero es una llamada a la biblioteca DLL en ws2_32.dll, esta última es una instrucción de ensamblaje BSWAP. Por lo tanto, si está escribiendo algún código dependiente de la plataforma, prefiera usar los intrínsecos para la velocidad:

#define htonl(x) _byteswap_ulong(x)

Esto puede ser especialmente importante para .Procesamiento de imágenes PNG donde todos los enteros se guardan en Big Endian con la explicación " Se puede usar htonl ()..."{para reducir la velocidad típica Programas de Windows, si no está preparado}.

 5
Author: user2699548,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-20 10:45:38

La mayoría de las plataformas tienen un archivo de encabezado del sistema que proporciona funciones de byteswap eficientes. En Linux está en <endian.h>. Puedes envolverlo muy bien en 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';
}

Salida:

cafe
deadbeaf
feeddeafbeefcafe
 4
Author: Maxim Egorushkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-07 18:02:01

Me gusta este, solo por estilo: -)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
 4
Author: friedemann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-26 22:13:17

En serio... No entiendo por qué todas las soluciones son complicado! ¿Qué tal la función de plantilla más simple y general que intercambia cualquier tipo de cualquier tamaño bajo cualquier circunstancia en cualquier 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]);
}

Es el poder mágico de C y C++ juntos! Simplemente cambie la variable original carácter por carácter.

Recuerde que no utilicé el operador de asignación simple " = " porque algunos objetos se estropearán cuando el endianness se voltea y el constructor de copia (u operador de asignación) no funcionará. Por lo tanto, es más confiable copiarlos char por char.

Para llamarlo, solo use

double x = 5;
SwapEnd(x);

Y ahora x es diferente en peso.

 4
Author: The Quantum Physicist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-29 16:12:25

Tengo este código que me permite convertir de HOST_ENDIAN_ORDER (lo que sea) a LITTLE_ENDIAN_ORDER o BIG_ENDIAN_ORDER. Utilizo una plantilla, así que si intento convertir de HOST_ENDIAN_ORDER a LITTLE_ENDIAN_ORDER y resultan ser los mismos para la máquina para la que compilo, no se generará código.

Aquí está el código con algunos comentarios:

// 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
Author: Mathieu Pagé,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-20 04:25:11

Si un entero sin signo big-endian de 32 bits se parece a 0xAABBCCDD que es igual a 2864434397, entonces ese mismo entero sin signo de 32 bits se parece a 0xDDCCBBAA en un procesador little-endian que también es igual a 2864434397.

Si un short sin signo big-endian de 16 bits se parece a 0xAABB que es igual a 43707, entonces ese mismo short sin signo de 16 bits se parece a 0xBBAA en un procesador little-endian que también es igual a 43707.

Aquí hay un par de útiles # definir funciones para intercambiar bytes de little-endian a big-endian y 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))
 3
Author: Adam Freeman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-06 22:35:58

Aquí hay una versión generalizada que se me ocurrió en la parte superior de la cabeza, para intercambiar un valor en su lugar. Las otras sugerencias serían mejores si el rendimiento es 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]);
    }

Descargo de responsabilidad: Todavía no he intentado compilar esto o probarlo.

 2
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-21 14:08:34

Si tomas el patrón común para invertir el orden de bits en una palabra, y cull la parte que invierte bits dentro de cada byte, entonces te quedas con algo que solo invierte los bytes dentro de una palabra. Para 64 bits:

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

El compilador debería limpiar las operaciones de enmascaramiento de bits superfluas (las dejé para resaltar el patrón), pero si no lo hace, puede reescribir la primera línea de esta manera:

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

Que normalmente debería simplificar a una sola rotación instrucción en la mayoría de las arquitecturas (ignorando que toda la operación es probablemente una instrucción).

En un procesador RISC las constantes grandes y complicadas pueden causar dificultades al compilador. Sin embargo, puede calcular trivialmente cada una de las constantes de la anterior. Así:

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

Si lo desea, puede escribirlo como un bucle. No será eficiente, pero solo por diversión:

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

Y para completar, aquí está la versión simplificada de 32 bits de la primera formulario:

x = ( x               << 16) ^  (x >> 16);
x = ((x & 0x00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff);
 2
Author: sh1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-20 11:51:33

Solo pensé que agregué mi propia solución aquí ya que no la he visto en ningún lugar. Es una pequeña y portable función templada de C++ y portable que solo utiliza operaciones de bits.

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;
}
 1
Author: Joao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-29 19:30:25

Con los siguientes códigos puede intercambiar fácilmente entre BigEndian y 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
Author: Pzy64,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-17 15:58:44

Estoy realmente sorprendido de que nadie haya mencionado las funciones htobeXX y betohXX. Se definen en endian.h y son muy similares a las funciones de red htonXX.

 1
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-11 19:15:28

Wow, no podía creer algunas de las respuestas que he leído aquí. Hay realmente una instrucción en la asamblea que hace esto más rápido que cualquier otra cosa. bswap. Simplemente podrías escribir una función como esta...

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

Es MUCHO más rápido que los intrínsecos que se han sugerido. Los he desmontado y he mirado. La función anterior no tiene prólogo / epílogo, por lo que prácticamente no tiene sobrecarga en absoluto.

unsigned long _byteswap_ulong(unsigned long value);

Hacer 16 bits es igual de fácil, con la excepción de que usa xchg al, ah. bswap solo funciona en registros de 32 bits.

64-bit es un poco más complicado, pero no demasiado. Mucho mejor que todos los ejemplos anteriores con bucles y plantillas, etc.

Hay algunas advertencias aquí... En primer lugar, bswap solo está disponible en CPU de 80x486 y superiores. ¿Alguien planea ejecutarlo en un 386?!? Si es así, aún puede reemplazar bswap con...

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

También el ensamblaje en línea solo está disponible en código x86 en Visual Studio. Una función desnuda no puede ser forrado y tampoco está disponible en versiones x64. I esa instancia, vas a tener que utilizar el compilador intrínsecos.

 0
Author: The Welder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-24 01:47:38

Técnica portátil para la implementación de accesores endian no alineados y no in situ amigables con el optimizador. Trabajan en cada compilador, cada alineación de límites y cada orden de bytes. Estas rutinas no alineadas se complementan, o se discuten, dependiendo del endiano nativo y la alineación. Listado parcial, pero usted consigue la idea. BO * son valores constantes basados en el orden nativo de bytes.

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

Estos typedefs tienen el beneficio de generar errores de compilador si no se usan con los accesores, mitigando así errores de acceso olvidados.

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 */
 0
Author: BSalita,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-23 08:53:38

Recientemente escribí una macro para hacer esto en C, pero es igualmente válida en 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)

Acepta cualquier tipo e invierte los bytes en el argumento pasado. Ejemplos de usos:

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

Que imprime:

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

Lo anterior es perfectamente capaz de copiar / pegar, pero hay mucho que hacer aquí, así que desglosaré cómo funciona pieza por pieza:

Lo primero notable es que toda la macro está encerrada en un bloque do while(0). Este es un idioma común para permitir uso normal de punto y coma después de la macro.

Lo siguiente es el uso de una variable llamada REVERSE_BYTES como contador del bucle for. El nombre de la macro en sí se usa como nombre de variable para garantizar que no choca con ningún otro símbolo que pueda estar en el ámbito donde se use la macro. Dado que el nombre se está utilizando dentro de la expansión de la macro, no se expandirá de nuevo cuando se use como nombre de variable aquí.

Dentro del bucle for, se hace referencia a dos bytes y XOR swapped (por lo que no se requiere un nombre de variable temporal):

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

__VA_ARGS__ representa lo que se le dio a la macro, y se utiliza para aumentar la flexibilidad de lo que se puede pasar (aunque no por mucho). La dirección de este argumento se toma y se envía a un puntero unsigned char para permitir el intercambio de sus bytes a través de la subíndice array [].

El punto peculiar final es la falta de {} llaves. No son necesarios porque todos los pasos en cada intercambio se unen con el operador de coma , haciéndoles una declaración.

Finalmente, vale la pena señalar que este no es el enfoque ideal si la velocidad es una prioridad. Si este es un factor importante, algunas de las macros específicas de tipo o directivas específicas de plataforma a las que se hace referencia en otras respuestas son probablemente una mejor opción. Este enfoque, sin embargo, es portable a todos los tipos, a todas las plataformas principales y a los lenguajes C y C++.

 0
Author: Ryan Hilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-17 07:27:03

Aquí le mostramos cómo leer un double almacenado en formato IEEE 754 de 64 bits, incluso si su computadora host utiliza un sistema diferente.

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

Para el resto del conjunto de funciones, incluidas las rutinas write y integer, consulte mi proyecto github

Https://github.com/MalcolmMcLean/ieee754

 0
Author: Malcolm McLean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-22 22:40:33

Intente Boost::endian, y NO LO IMPLEMENTE USTED MISMO!

Aquí hay un enlace

 -1
Author: morteza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-22 22:16:19

Busque bit shifting, ya que esto es básicamente todo lo que necesita hacer para cambiar de little -> big endian. Luego, dependiendo del tamaño de bits, cambia la forma en que realiza el cambio de bits.

 -4
Author: Redbaron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 20:30:37