Как преобразовать значения big-endian и little-endian в С++?

Как преобразовать значения big-endian и little-endian в С++?

EDIT: для ясности я должен перевести двоичные данные (значения с плавающей запятой с двойной точностью и 32-разрядные и 64-разрядные целые числа) из одной архитектуры процессора в другую. Это не связано с сетью, поэтому ntoh() и подобные функции здесь не работают.

EDIT # 2: Ответ, который я принял, относится непосредственно к компиляторам, на которых я нацелен (именно поэтому я его выбрал). Однако здесь есть и другие очень хорошие, более портативные ответы.

Ответ 1

Если вы используете Visual С++, выполните следующие действия: включите intrin.h и вызовите следующие функции:

Для 16-разрядных номеров:

unsigned short _byteswap_ushort(unsigned short value);

Для 32-разрядных номеров:

unsigned long _byteswap_ulong(unsigned long value);

Для 64-разрядных номеров:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8-битные номера (символы) не нужно преобразовывать.

Также они определены только для неподписанных значений, которые они также используют для целых чисел со знаком.

Для поплавков и удваивает его сложнее, чем для простых целых чисел, поскольку они могут или не могут быть в байтовом порядке хост-машин. Вы можете получить малоподобные поплавки на машинах большого конца и наоборот.

Другие компиляторы также имеют схожие функции.

В GCC, например, вы можете напрямую позвонить:

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

(нет необходимости включать что-либо). Afaik bits.h объявляет ту же функцию не с gcc-ориентированным способом.

16-битная сводка просто немного повернуть.

Вызов встроенных функций вместо того, чтобы сворачивать ваши собственные, дает вам наилучшую производительность и плотность кодов btw..

Ответ 2

Проще говоря:

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

: swap_endian<uint32_t>(42).

Ответ 3

От Ошибка байтового порядка: Rob Pyke:

Предположим, что ваш поток данных имеет 32-разрядное целое число с кодировкой с маленьким энтиансом. Здесь, как извлечь его (предполагая неподписанные байты):

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

Если это big-endian, вот как его извлечь:

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

TL; DR: не беспокойтесь о собственном заказе вашей платформы, все, что считается, - это порядок байтов потока, из которого вы читаете, и вам лучше надеяться, что он будет хорошо определен.

Примечание: в комментарии было отмечено, что при отсутствии явного преобразования типов важно, чтобы data был массивом unsigned char или uint8_t. Использование signed char или char (если подписано) приведет к продвижению data[x] к целому числу и data[x] << 24, потенциально меняющему 1 на знаковый бит, который является UB.

Ответ 4

Если вы делаете это для обеспечения совместимости с сетью/хостом, вы должны использовать:

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)

Если вы делаете это по какой-то другой причине, один из представленных здесь решений byte_swap будет работать нормально.

Ответ 5

Я сделал несколько предложений из этого сообщения и собрал их вместе, чтобы сформировать это:

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

Ответ 6

Процедура перехода от широкоформатного к малодунному - это то же самое, что и переход от мало-северного к великому.

Вот пример кода:

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

Ответ 7

Существует инструкция по сборке под названием BSWAP, которая будет выполнять своп для вас, очень быстро. Вы можете прочитать об этом здесь.

Visual Studio, или, точнее, библиотека времени исполнения Visual С++, имеет встроенные функции платформы, называемые _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Подобное должно существовать для других платформ, но я не знаю, как они будут называться.

Ответ 8

Мы сделали это с помощью шаблонов. Вы можете сделать что-то вроде этого:

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

Ответ 9

Если вы делаете это для передачи данных между различными платформами, смотрите функции ntoh и hton.

Ответ 10

Точно так же вы делаете в C:

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

Вы также можете объявить вектор unsigned chars, memcpy входного значения в него, перевернуть байты в другой вектор и memcpy байты, но это займет порядка больше, чем бит-twiddling, особенно с 64- бит.

Ответ 11

В большинстве систем POSIX (через него нет в стандарте POSIX) существует файл endian.h, который можно использовать для определения того, какую кодировку использует ваша система. Оттуда это примерно так:

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

Это меняет порядок (с обратного порядка байтов на обратный):

Если у вас есть число 0xDEADBEEF (в системе с прямым порядком байтов, сохраненной как 0xEFBEADDE), ptr [0] будет 0xEF, ptr [1] будет 0xBE и т.д.

Но если вы хотите использовать его для работы в сети, то htons, htonl и htonll (и их обратные ntohs, ntohl и ntohll) будут полезны для преобразования порядка хостов в порядок сетей.

Ответ 12

Обратите внимание, что, по крайней мере, для Windows, htonl() намного медленнее, чем их собственный экземпляр _byteswap_ulong(). Первый - это вызов библиотеки DLL в файл ws2_32.dll, последний - одна инструкция сборки BSWAP. Поэтому, если вы пишете какой-то платформозависимый код, предпочитайте использовать встроенные функции для скорости:

#define htonl(x) _byteswap_ulong(x)

Это может быть особенно важно для обработки изображений .PNG, где все целые числа сохраняются в Big Endian с объяснением "Можно использовать htonl()..." {для замедления типичных программ Windows, если вы не подготовлены}.

Ответ 13

На большинстве платформ есть системный заголовочный файл, который обеспечивает эффективные функции byteswap. В Linux он находится в <endian.h>. Вы можете легко его обернуть в С++:

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

Вывод:

cafe
deadbeaf
feeddeafbeefcafe

Ответ 14

Мне нравится этот, только для стиля: -)

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

Ответ 15

Серьезно... Я не понимаю, почему все решения являются сложными! Как насчет простейшей, самой общей функции шаблона, которая меняет любой тип любого размера при любых обстоятельствах в любой операционной системе?

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

Это волшебная сила C и С++ вместе! Просто замените исходный символ переменной символом.

Помните, что я не использовал простой оператор присваивания "=", потому что некоторые объекты будут перепутаны, когда энтитантность будет перевернута, а конструктор копирования (или оператор присваивания) не будет работать. Поэтому более надежно копировать их char на char.

Чтобы вызвать его, просто используйте

double x = 5;
SwapEnd(x);

а теперь x различается по контенту.

Ответ 16

У меня есть этот код, который позволяет мне преобразовать из HOST_ENDIAN_ORDER (что бы это ни было) в LITTLE_ENDIAN_ORDER или BIG_ENDIAN_ORDER. Я использую шаблон, поэтому, если я попытаюсь преобразовать из HOST_ENDIAN_ORDER в LITTLE_ENDIAN_ORDER, и они будут одинаковыми для машины, для которой я компилирую, никакой код не будет создан.

Вот код с комментариями:

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

Ответ 17

Если 32-разрядное целое число без знака выглядит как 0xAABBCCDD, равное 2864434397, то такое же 32-разрядное целое число без знака выглядит как 0xDDCCBBAA на процессоре little-endian, который также равен 2864434397.

Если big-endian 16-битный unsigned short выглядит как 0xAABB, который равен 43707, то тот же 16-разрядный беззнаковый короткий выглядит как 0xBBAA на процессоре little-endian, который также равен 43707.

Вот несколько удобных функций #define для обмена байтами от little-endian до big-endian и наоборот →

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

Ответ 18

Здесь обобщенная версия, которую я придумал с головы, заменил значение на месте. Другие предложения были бы лучше, если производительность была проблемой.

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

Отказ от ответственности: Я еще не пытался скомпилировать это или протестировать его.

Ответ 19

Если вы берете общий шаблон для реверсирования порядка бит в слове и отбираете часть, которая меняет биты в каждом байте, тогда у вас остается что-то, что только отменяет байты внутри слова. Для 64-бит:

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

Компилятор должен очистить лишние операции маскировки (я оставил их, чтобы выделить шаблон), но если это не так, вы можете переписать первую строку следующим образом:

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

Это обычно упрощает до одной команды поворота на большинстве архитектур (игнорируя, что вся операция, вероятно, одна инструкция).

В RISC-процессоре большие, сложные константы могут вызвать трудности с компилятором. Однако вы можете тривиально вычислить каждую из констант из предыдущей. Например:

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

Если вам нравится, вы можете написать это как цикл. Это не будет эффективно, но просто для удовольствия:

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

И для полноты здесь упрощенная 32-битная версия первой формы:

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

Ответ 20

Просто подумал, что я добавил свое собственное решение здесь, так как я его нигде не видел. Это небольшая и портативная С++-шаблонная функция и переносная, которая использует только бит-операции.

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

Ответ 21

Используя приведенные ниже коды, вы можете легко переключаться между BigEndian и 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))

Ответ 22

Недавно я написал макрос для этого в 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)

Он принимает любой тип и меняет байты в переданном аргументе. Пример использования:

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

Какие принты:

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

Вышеописанное прекрасно копируется/вставляется, но здесь многое происходит, поэтому я разбиваю, как он работает по частям:

Первое примечательно, что весь макрос заключен в блок do while(0). Это общая идиома, чтобы разрешить использование обычной точки с запятой после макроса.

Далее следует использование переменной с именем REVERSE_BYTES в качестве счетчика циклов for. Имя самого макроса используется как имя переменной, чтобы гарантировать, что оно не сталкивается с любыми другими символами, которые могут быть в области, где используется макрос. Поскольку имя используется в расширении макроса, оно не будет расширяться снова при использовании в качестве имени переменной здесь.

В цикле for есть два байта, на которые ссылаются, и XOR обменивается (поэтому имя временной переменной не требуется):

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

__VA_ARGS__ представляет все, что было передано макросу, и используется для повышения гибкости того, что может быть передано (хотя и не по большому счету). Затем адрес этого аргумента берется и преобразуется в указатель unsigned char, чтобы разрешить свопинг его байтов с помощью подстроки массива [].

Конечным своеобразным моментом является отсутствие брекетов {}. Они не нужны, потому что все шаги в каждом свопе объединены с оператором запятой , что делает их одним утверждением.

Наконец, стоит отметить, что это не идеальный подход, если скорость является главным приоритетом. Если это важный фактор, некоторые из макросов типа или специфических для платформы директив, упомянутых в других ответах, скорее всего, являются лучшим вариантом. Однако этот подход является переносимым для всех типов, всех основных платформ и языков C и С++.

Ответ 23

Я действительно удивлен, что никто не упоминает функции htobeXX и betohXX. Они определены в endian.h и очень похожи на сетевые функции htonXX.

Ответ 24

Ничего себе, я не мог поверить некоторым из ответов, которые я прочитал здесь. На самом деле есть инструкция в сборке, которая делает это быстрее, чем что-либо еще. BSWAP. Вы могли бы просто написать такую ​​функцию...

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

Это намного быстрее, чем предположения, которые были предложены. Я разобрал их и посмотрел. Вышеупомянутая функция не имеет пролога/эпилога, так что практически нет никаких накладных расходов.

unsigned long _byteswap_ulong(unsigned long value);

Выполнение 16 бит так же просто, за исключением того, что вы будете использовать xchg al, ah. bswap работает только с 32-разрядными регистрами.

64-бит немного сложнее, но не слишком. Намного лучше, чем все вышеприведенные примеры с циклами и шаблонами и т.д.

Здесь есть некоторые оговорки... Во-первых, bswap доступен только на процессоре 80x486 и выше. Кто-нибудь планирует запустить его на 386?!? Если это так, вы все равно можете заменить bswap на...

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

Также встроенная сборка доступна только в коде x86 в Visual Studio. Голая функция не может быть выровнена, а также недоступна в сборках x64. В этом случае вам придется использовать встроенные функции компилятора.

Ответ 25

Портативная техника для внедрения оптимизаторов-дружественных нестандартных не-inplace endian-аксессуаров. Они работают на каждом компиляторе, каждом выравнивании границ и порядке каждого байта. Эти невыложенные подпрограммы дополняются или обсуждаются в зависимости от исходного кода и выравнивания. Частичный листинг, но вы получите эту идею. BO * - постоянные значения, основанные на упорядочении нативного байта.

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

Эти typedefs имеют преимущество в повышении ошибок компилятора, если они не используются с аксессуарами, что уменьшает ошибки забытых аксессуаров.

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

Ответ 26

Попробуйте Boost::endian и НЕ ВЫПОЛНЯЙТЕ ЭТО СЕБЯ!

Здесь ссылка

Ответ 27

Здесь, как читать двойной, сохраненный в 64-битном формате IEEE 754, даже если ваш хост-компьютер использует другую систему.

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

Для остальной части набора функций, включая записи и целые подпрограммы, см. мой проект github

https://github.com/MalcolmMcLean/ieee754

Ответ 28

Замена байтов трюком ye olde 3-step-xor вокруг центра в функции шаблона дает гибкое, быстрое решение O (ln2), которое не требует библиотеки, стиль здесь также отклоняет типы 1 байта:

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

Ответ 29

Похоже, безопасным способом было бы использовать htons для каждого слова. Итак, если у вас есть...

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

Выше было бы не работать, если бы вы работали в системе с прямым порядком байтов, поэтому я бы искал то, что ваша платформа использует в качестве условия времени компиляции, чтобы решить, является ли htons запретом. В конце концов, это O (n). На Mac это было бы что-то вроде...

#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

Ответ 30

Посмотрите на бит-сдвиг, так как это в основном все, что вам нужно сделать, чтобы поменять местами немного → большой endian. Затем, в зависимости от размера бита, вы изменяете способ смещения бит.