it-swarm-eu.dev

Jak lze převést mezi velkými endianovými a malými endianovými hodnotami v C++?

Jak lze převést mezi velkými endianovými a malými endianovými hodnotami v C++?

EDIT: Pro přehlednost musím přeložit binární data (hodnoty s plovoucí desetinnou čárkou s dvojitou přesností a 32bitové a 64bitové celá čísla) z jedné architektury CPU na druhou. To nezahrnuje vytváření sítí, takže ntoh () a podobné funkce zde nebudou fungovat.

EDIT # 2: Odpověď, kterou jsem přijal, platí přímo pro kompilátory, na které cílím (což je důvod, proč jsem si to vybral). Existují však i další velmi dobré, přenosnější odpovědi.

176
Uhall

Pokud používáte Visual C++ proveďte následující: Zahrnout intrin.h a zavolat následující funkce:

Pro 16bitová čísla:

unsigned short _byteswap_ushort(unsigned short value);

Pro 32 bitová čísla:

unsigned long _byteswap_ulong(unsigned long value);

Pro 64bitová čísla:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 bitových čísel (znaků) není třeba převádět.

Také tyto jsou definovány pouze pro nepodepsané hodnoty, které pracují pro podepsaná celá čísla.

Pro plováky a čtyřhry je to obtížnější jako u jednoduchých celých čísel, protože tyto mohou nebo nemusí být v pořadí bajtů hostitelských počítačů. Na big-endian strojích můžete získat malé endian plováky a naopak.

Ostatní kompilátory mají také podobné vnitřní vlastnosti. 

VGCCnapříklad můžete přímo volat:

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

(není třeba něco zahrnout). Afaik bits.h deklaruje stejnou funkci také neccc centrickým způsobem.

16 bit swap je to jen bit-točit.

Výzva k intrinsics namísto kvalování vlastní vám dává nejlepší výkon a hustotu kódu btw .. 

147
Nils Pipenbrinck

Jednoduše řečeno:

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

použití: swap_endian<uint32_t>(42).

76
Alexandre C.

Od Byte Order Fallacy Autor Rob Pyke:

Řekněme, že váš datový proud má 32-bitové celé číslo kódované malým endianem. Zde je postup, jak jej extrahovat (za předpokladu, že se jedná o nepodepsané bajty):

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

Pokud je to big-endian, zde je návod, jak jej extrahovat:

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

TL; DR: se nemusíte starat o nativní pořadí vaší platformy, vše, co se počítá, je pořadí bajtů proudu, ze kterého čtete, a raději doufáte, že je to dobře definované.

Poznámka: to bylo poznamenané v komentáři, který chybí explicitní typová konverze, to bylo důležité, že data být pole unsigned char nebo uint8_t. Použití signed char nebo char (jestliže podepsaný) vyústí v data[x] být povýšený na celé číslo a data[x] << 24 potenciálně posun 1 do znakového bitu, který je UB.

64
Matthieu M.

Pokud to děláte pro účely kompatibility sítě/hostitele, měli byste použít:

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)

Pokud to děláte z nějakého jiného důvodu, jedno z řešení byte_swap, které je zde uvedeno, bude fungovat dobře.

48
Frosty

Z tohoto příspěvku jsem udělal několik návrhů a vytvořil (a) je dohromady:

#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
Steve Lorimer

K dispozici je instrukce shromáždění nazvaná BSWAP, která provede výměnu za vás, extrémně rychlá. Můžete o ní číst zde .

Visual Studio, přesněji Visual C++ runtime knihovna, má pro tuto platformu vnitřní vlastnosti, tzv. _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Podobné by měly existovat i pro jiné platformy, ale nevím, co by se jim říkalo.

15
anon6439

Postup přechodu z big-endian na little-endian je stejný jako přechod z maličkého endianu na big-endiana.

Zde je příklad kódu:

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

Udělali jsme to se šablonami. Můžete něco takového udělat:

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

Pokud to děláte pro přenos dat mezi různými platformami, podívejte se na funkce ntoh a hton.

8
Andrew

Stejně jako v C:

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

Můžete také deklarovat vektor nepodepsaných znaků, zapsat do něj vstupní hodnotu, vrátit bajty do jiného vektoru a memcpy bajtů ven, ale to bude trvat řády delší než bit twiddling, zejména s 64bitovými hodnotami.

6
Ben Straub

Na většině systémů POSIX (přes to není v POSIX standardu) existuje endian.h, který lze použít k určení toho, jaké kódování váš systém používá. Odtud je to něco takového:

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

Toto zamění pořadí (od big-endian k malému endian):

Pokud máte číslo 0xDEADBEEF (na malém endian systému uloženém jako 0xEFBEADDE), ptr [0] bude 0xEF, ptr [1] je 0xBE, atd.

Pokud je však chcete použít pro síť, pak htons, htonl a htonll (a jejich inverzní ntohs, ntohl a ntohll) budou užitečné pro převod z pořadí hostitele na síťové pořadí.

6
terminus

Všimněte si, že alespoň pro Windows je htonl () mnohem pomalejší než jejich vnitřní protějšek _byteswap_ulong (). První z nich je volání DLL knihovny do ws2_32.dll, druhé je jedna instrukce BSWAP Assembly. Pokud tedy píšete nějaký kód závislý na platformě, raději používejte funkce pro rychlost:

#define htonl(x) _byteswap_ulong(x)

To může být obzvláště důležité pro zpracování obrazu .PNG, kde jsou všechna velká čísla uložena ve formátu Big Endian s vysvětlením "Jeden může použít htonl () ..." {ke zpomalení typických programů Windows, pokud nejste připraveni}.

5
user2699548

Vážně ... Nechápu, proč jsou všechna řešení takovákomplikovaná! A co nejjednodušší, nejobecnější funkce šablony, která vymění jakýkoli typ jakékoli velikosti za všech okolností v každém operačním systému ????

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

Je to magická síla C a C++ dohromady! Jednoduše zaměňte původní variabilní znak za znak.

Nezapomeňte, že jsem nepoužíval jednoduchý operátor přiřazení "=", protože některé objekty budou při převrácení endiaty vyřazeny a konstruktor kopírování (nebo operátor přiřazení) nebude fungovat. Proto je spolehlivější kopírovat je char podle char.

Chcete-li volat, stačí použít

double x = 5;
SwapEnd(x);

a nyní x se liší v endianness.

líbí se mi tohle, jen pro styl :-)

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

Většina platforem má soubor záhlaví systému, který poskytuje efektivní funkce bajtů. V Linuxu je v <endian.h>. Můžete to pěkně zabalit v 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';
}

Výstup:

cafe
deadbeaf
feeddeafbeefcafe
4
Maxim Egorushkin

Pokud big-endian 32bitové celé číslo bez znaménka vypadá jako 0xAABBCCDD, které se rovná 2864434397, pak stejné 32bitové celé číslo bez znaménka vypadá jako 0xDDCCBBAA na procesoru typu little-endian, který je roven 2864434397.

Pokud big-endian 16-bitový unsigned short vypadá jako 0xAABB, což je rovno 43707, pak ten stejný 16-bitový krátký nepodepsaný krátký vypadá jako 0xBBAA na malém endian procesoru, který je roven 43707.

Zde je pár užitečných funkcí #define pro přepínání bajtů z small-endian na big-endian a naopak ->

// 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
Adam Freeman

Mám tento kód, který mi umožňuje převést z hostitele_ENDIAN_ORDER (cokoliv) na LITTLE_ENDIAN_ORDER nebo BIG_ENDIAN_ORDER. Používám šablonu, takže pokud se pokusím převést z host_ENDIAN_ORDER na LITTLE_ENDIAN_ORDER a jsou pro počítač, pro který skládám, shodné, nebude vygenerován žádný kód.

Zde je kód s komentáři:

// 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
Mathieu Pagé

Myslel jsem, že jsem sem přidal vlastní řešení, protože jsem ho nikde neviděl. Je to malá a přenosná C++ templated funkce a přenosný, který používá pouze bit operace.

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

Tady je zobecněná verze, kterou jsem přišel s horní částí hlavy, za to, že jsem si vyměnil hodnotu. Další návrhy by byly lepší, pokud by výkon byl problém.

 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: / Nesnažil jsem se to zkompilovat nebo ještě otestovat.

2
Mark Ransom

Pokud vezmete společný vzor pro obrácení pořadí bitů v aplikaci Word a vyřadíte část, která obrací bity v každém bajtu, pak zůstane s něčím, co pouze převede bajty v rámci aplikace Word. Pro 64 bitů:

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

Kompilátor by měl vyčistit nadbytečné operace bit-maskování (nechal jsem je, abych zvýraznil vzor), ale pokud ne, můžete přepsat první řádek tímto způsobem:

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

To by mělo na většině architektur běžně zjednodušit až na jednu rotační instrukci (ignoruje se, že celá operace je pravděpodobně jedna instrukce).

Na procesoru RISC mohou velké, komplikované konstanty způsobit potíže kompilátoru. Každou z konstant však můžete triviálně vypočítat z předchozí konstanty. Jako tak:

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

Pokud chcete, můžete to napsat jako smyčku. Nebude to efektivní, ale jen pro zábavu:

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

A pro úplnost je zde zjednodušená 32bitová verze prvního formuláře:

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

Jsem opravdu překvapen žádným z uvedených funkcí htobeXX a betohXX. Jsou definovány v endian.h a jsou velmi podobné síťovým funkcím htonXX.

1
Nick

S níže uvedenými kódy můžete snadno vyměnit mezi Big Endian a Little Endian 

#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
pz64_

Zdá se, že bezpečným způsobem by bylo použití htonů na každém Wordu. Takže, pokud máte ...

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

Výše uvedené by bylo no-op, kdybyste byli na big-endian systému, takže bych se podívat na to, co vaše platforma používá jako kompilace-podmínka rozhodnout, zda htons je no-op. Koneckonců je O(n). Na Macu by to bylo něco jako ...

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

Přenosná technika pro implementaci optimalizovaných nepřipojených endian accessorů. Pracují na každém kompilátoru, na každém zarovnání hranic a na každém pořadí bytů. Tyto nezměněné rutiny jsou doplněny nebo diskutovány v závislosti na nativním endianu a zarovnání. Částečný výpis, ale dostanete nápad. BO * jsou konstantní hodnoty založené na nativním pořadí bytů.

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

Tyto typedefs mají výhodu v tom, že zvyšují chyby kompilátoru, pokud nejsou používány s přístupovými právy, což zmírňuje zapomenuté chyby v přístupových bodech.

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
BSalita

Nedávno jsem napsal makro, abych to provedl v jazyce C, ale je to stejně platné v jazyce 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)

Přijímá libovolný typ a převádí bajty v předaném argumentu. Příklad použití:

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

Které výtisky:

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

Výše uvedené je dokonale kopírovat/vkládat, ale je tu spousta děje, takže rozdělím, jak to funguje po kousku:

První pozoruhodná věc je, že celé makro je uzavřeno v bloku do while(0). Toto je obyčejný idiom dovolit normální středníkové použití po makru.

Další je použití proměnné s názvem REVERSE_BYTES jako čítač for loop. Samotné jméno makra se používá jako název proměnné, aby se zajistilo, že nebude kolidovat s jinými symboly, které mohou být v rozsahu, kde se makro používá. Vzhledem k tomu, že název je používán v rámci expanze makra, nebude znovu rozšířen, pokud bude použit jako název proměnné.

V rámci smyčky for jsou odkazovány dva bajty a XOR je zaměněno (takže není vyžadován dočasný název proměnné):

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

__VA_ARGS__ představuje cokoliv, co bylo makro uděleno, a používá se ke zvýšení flexibility toho, co může být předáno (i když ne moc). Adresa tohoto argumentu je pak převzata a vložena do ukazatele unsigned char, aby bylo možné přepínat jeho bajty přes pole [] subscripting.

Konečným zvláštním bodem je nedostatek závorek {}. Nejsou nutné, protože všechny kroky v každém swapu jsou spojeny s operátorem comma , což z nich dělá jeden příkaz.

Konečně stojí za zmínku, že to není ideální přístup, pokud je rychlost prioritou. Pokud se jedná o důležitý faktor, některá makra specifická pro daný typ písma nebo direktivy specifické pro danou platformu uvedené v jiných odpovědích jsou pravděpodobně lepší možností. Tento přístup je však přenosný do všech typů, všech hlavních platforem a jazyků C i C++.

0
Ryan Hilbert

Zde je návod, jak číst dvojité uložené v 64 bitovém formátu IEEE 754, i když váš počítač používá jiný systém.

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

Pro zbytek sady funkcí, včetně zápisu a celočíselných rutin viz můj projekt github

https://github.com/MalcolmMcLean/ieee754

0
Malcolm McLean

Byte swap s ye olde 3-krok-xor trik kolem pivot ve funkci šablony dává flexibilní, rychlé O(ln2) řešení, které nevyžaduje knihovnu, styl zde také odmítá 1 byte typy:

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

Vyzkoušejte Boost::endian a NEVYKLÁDEJTE SE!

Zde je odkaz

0
morteza

Wow, nemohla jsem uvěřit některým odpovědím, které jsem zde četla. Ve shromáždění je vlastně instrukce, která to dělá rychleji než cokoliv jiného. bswap. Můžete jednoduše napsat funkci jako je tato ...

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

To jeMNOHEMrychlejší než vnitřní, které byly navrženy. Rozebral jsem je a podíval se. Výše uvedená funkce nemá žádný prolog/epilog, takže nemá prakticky žádnou režii.

unsigned long _byteswap_ulong(unsigned long value);

Dělat 16 bitů je stejně snadné, s výjimkou, že byste použili xchg al, ah. bswap funguje pouze na 32bitových registrech.

64-bit je o něco složitější, ale ne příliš. Mnohem lepší než všechny výše uvedené příklady smyček a šablon atd.

Tam jsou některé výhrady zde ... Za prvé bswap je k dispozici pouze na 80x486 CPU a vyšší. Plánuje to někdo na 386?!? Pokud ano, můžete bswap nahradit ...

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

Také inline shromáždění je k dispozici pouze v kódu x86 v aplikaci Visual Studio. Nahou funkci nelze lemovat a také není k dispozici v sestavách x64. I v tomto případě budete muset použít kompilátor.

0
The Welder