it-swarm.com.de

Wie konvertiere ich zwischen Big-Endian- und Little-Endian-Werten in C++?

Wie konvertiere ich zwischen Big-Endian- und Little-Endian-Werten in C++?

BEARBEITEN: Zum besseren Verständnis muss ich binäre Daten (Gleitkommawerte mit doppelter Genauigkeit und 32-Bit- und 64-Bit-Ganzzahlen) von einer CPU-Architektur in eine andere übersetzen. Dies beinhaltet kein Networking, daher funktionieren hier ntoh () und ähnliche Funktionen nicht.

BEARBEITUNG 2: Die Antwort, die ich angenommen habe, gilt direkt für Compiler, die ich anvisiere (weshalb ich sie gewählt habe). Es gibt jedoch auch andere sehr gute, tragbarere Antworten.

176
Uhall

Wenn Sie Visual C++ verwenden, gehen Sie wie folgt vor: Sie fügen intrin.h ein und rufen die folgenden Funktionen auf:

Für 16-Bit-Nummern:

unsigned short _byteswap_ushort(unsigned short value);

Für 32-Bit-Nummern:

unsigned long _byteswap_ulong(unsigned long value);

Für 64-Bit-Nummern:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8-Bit-Zahlen (Zeichen) müssen nicht konvertiert werden.

Auch diese sind nur für vorzeichenlose Werte definiert, die auch für vorzeichenbehaftete Ganzzahlen funktionieren.

Für Floats und Doubles ist dies schwieriger als bei einfachen Ganzzahlen, da sich diese möglicherweise in der Byte-Reihenfolge der Host-Maschinen befinden. Sie können Little-Endian-Floats auf Big-Endian-Maschinen erhalten und umgekehrt.

Andere Compiler haben auch ähnliche Intrinsics. 

InGCCkönnen Sie beispielsweise direkt anrufen:

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

(Keine Notwendigkeit etwas hinzuzufügen). Afaik bits.h deklariert die gleiche Funktion auch auf nicht gcc-zentrische Weise.

16-Bit-Swap ist nur eine kleine Drehung.

Wenn Sie die Intrinsics aufrufen, anstatt selbst zu rollen, erhalten Sie die beste Leistung und Codedichte. 

147

Einfach gesagt:

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

verwendung: swap_endian<uint32_t>(42).

76
Alexandre C.

Aus The Byte Order Fallacy von Rob Pyke:

Nehmen wir an, Ihr Datenstrom hat eine 32-Bit-Ganzzahl, die im Little Endian-Format codiert ist. So extrahieren Sie es (unter der Annahme von unsignierten Bytes):

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

Wenn es sich um einen Big-Endian handelt, können Sie ihn wie folgt extrahieren:

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

TL; DR: Machen Sie sich keine Sorgen über Ihre native Plattform-Reihenfolge. Alles, was zählt, ist die Bytereihenfolge des Streams, aus dem Sie lesen, und Sie hoffen, dass sie gut definiert ist.

Hinweis: In dem Kommentar wurde bemerkt, dass ohne explizite Typkonvertierung data ein Array von unsigned char oder uint8_t ist. Die Verwendung von signed char oder char (falls signiert) führt dazu, dass data[x] möglicherweise zu einer Ganzzahl und data[x] << 24 befördert wird Verschieben einer 1 in das Vorzeichenbit, das UB ist.

64
Matthieu M.

Wenn Sie dies aus Gründen der Netzwerk-/Host-Kompatibilität tun, sollten Sie Folgendes verwenden:

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)

Wenn Sie dies aus einem anderen Grund tun, würde eine der hier vorgestellten Lösungen von byte_swap gut funktionieren.

48
Frosty

Ich habe ein paar Vorschläge aus diesem Beitrag genommen und sie zu diesem zusammengefügt:

#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

Es gibt eine Assembly-Anweisung namens BSWAP, die den Swap für Sie erledigt, extrem schnell . Sie können darüber hier nachlesen.

Visual Studio, oder genauer gesagt die Visual C++ - Laufzeitbibliothek, verfügt über Plattform-Intrinsics, die als _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64() bezeichnet werden. Ähnliches sollte für andere Plattformen existieren, aber ich weiß nicht, wie sie genannt werden würden.

15
anon6439

Das Verfahren, um vom Big-Endian zum Little-Endian zu gehen, ist das gleiche wie beim Little-Endian zum Big-Endian.

Hier ist ein Beispielcode:

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

Wir haben das mit Vorlagen gemacht. Sie könnten so etwas wie folgt:

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

Wenn Sie dies tun, um Daten zwischen verschiedenen Plattformen zu übertragen, schauen Sie sich die Funktionen von ntoh und hton an.

8
Andrew

Genauso wie in C:

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

Sie können auch einen Vektor vorzeichenloser Zeichen deklarieren, den Eingabewert darin speichern, die Bytes in einen anderen Vektor umkehren und die Bytes ausdrücken, aber das dauert Größenordnungen länger als das Bit Twiddling, insbesondere bei 64-Bit-Werten.

6
Ben Straub

Auf den meisten POSIX-Systemen (durch diese nicht im POSIX-Standard) gibt es die endian.h, mit der Sie feststellen können, welche Kodierung Ihr System verwendet. Von da ist es so etwas:

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

Dies tauscht die Reihenfolge (von Big Endian zu Little Endian):

Wenn Sie die Nummer 0xDEADBEEF haben (auf einem Little-Endian-System als 0xEFBEADDE gespeichert), ist ptr [0] 0xEF, ptr [1] ist 0xBE usw.

Wenn Sie es jedoch für das Networking verwenden möchten, sind htons, htonl und htonll (und deren inverse ntohs, ntohl und ntohll) hilfreich bei der Konvertierung von Host-Reihenfolge zu Netzwerk-Reihenfolge.

6
terminus

Beachten Sie, dass htonl () zumindest für Windows viel langsamer ist als ihr intrinsisches Gegenstück _byteswap_ulong (). Ersteres ist ein Bibliotheksaufruf von DLL in ws2_32.dll, letzteres ist eine BSWAP-Assembly-Anweisung. Wenn Sie plattformabhängigen Code schreiben, empfiehlt es sich daher, die Intrinsics für Geschwindigkeit zu verwenden:

#define htonl(x) _byteswap_ulong(x)

Dies kann besonders wichtig für die .PNG-Bildverarbeitung sein, bei der alle Ganzzahlen in Big Endian mit der Erklärung "Man kann htonl () ..." {speichern, um typische Windows-Programme zu verlangsamen, wenn Sie nicht vorbereitet sind}.

5
user2699548

Im Ernst ... ich verstehe nicht, warum alle Lösungen so sind:kompliziert! Wie wäre es mit der einfachsten und allgemeinsten Template-Funktion, die jeden Typ jeder Größe unter allen Umständen in ein Betriebssystem tauscht ????

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 ist die magische Kraft von C und C++ zusammen! Tauschen Sie einfach die ursprüngliche Variable zeichenweise aus.

Denken Sie daran, dass ich den einfachen Zuweisungsoperator "=" nicht verwendet habe, da einige Objekte durcheinander gebracht werden, wenn die Endanwendung umgedreht wird und der Kopierkonstruktor (oder der Zuweisungsoperator) nicht funktioniert. Daher ist es zuverlässiger, sie char für char zu kopieren.

Um es anzurufen, benutzen Sie einfach

double x = 5;
SwapEnd(x);

und jetzt x unterscheidet sich in der Endianness.

ich mag dieses, nur für den Stil :-)

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

Die meisten Plattformen verfügen über eine Systemheaderdatei, die effiziente Byteswap-Funktionen bietet. Unter Linux ist es in <endian.h>. Sie können es schön in C++ einwickeln:

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

Ausgabe:

cafe
deadbeaf
feeddeafbeefcafe
4

Wenn eine 32-Bit-Ganzzahl mit Big-Endian-Zeichen wie 0xAABBCCDD aussieht, die 2864434397 entspricht, dann sieht diese 32-Bit-Ganzzahl ohne Vorzeichen auf einem Little-Endian-Prozessor wie 0xDDCCBBAA aus.

Wenn ein 16-Bit-Big-Endian-Kurzschluss wie 0xAABB (43707) aussieht, sieht der gleiche 16-Bit-Kurzschluss ohne Vorzeichen auf einem Little-Endian-Prozessor wie 0xBBAA aus, der ebenfalls 43707 entspricht.

Hier sind ein paar praktische Funktionen zum Definieren von Bytes von Little Endian zu Big Endian und umgekehrt ->

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

Ich habe diesen Code, mit dem ich von Host_ENDIAN_ORDER (was auch immer es ist) in LITTLE_ENDIAN_ORDER oder BIG_ENDIAN_ORDER konvertieren kann. Ich verwende eine Vorlage. Wenn ich also versuche, von Host_ENDIAN_ORDER nach LITTLE_ENDIAN_ORDER zu konvertieren, und sie für die Maschine, für die ich kompiliere, identisch sind, wird kein Code generiert.

Hier ist der Code mit einigen Kommentaren:

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

Ich dachte nur, ich habe hier meine eigene Lösung hinzugefügt, da ich sie nirgendwo gesehen habe. Es ist eine kleine und tragbare C++ - Funktion, die nur Bitoperationen verwendet.

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

Hier ist eine verallgemeinerte Version, die ich aus dem Kopf bekommen habe, um einen Wert zu ersetzen. Die anderen Vorschläge wären besser, wenn die Leistung ein Problem darstellt.

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

Haftungsausschluss: Ich habe noch nicht versucht, das zu kompilieren oder zu testen.

2
Mark Ransom

Wenn Sie das übliche Muster zum Umkehren der Reihenfolge der Bits in einem Word verwenden und den Teil aussortieren, der die Bits in jedem Byte umkehrt, dann bleibt etwas übrig, das nur die Bytes in einem Word umkehrt. Für 64 Bits:

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

Der Compiler sollte die überflüssigen Bitmaskierungsoperationen bereinigen (ich habe sie gelassen, um das Muster hervorzuheben), aber wenn dies nicht der Fall ist, können Sie die erste Zeile auf diese Weise neu schreiben:

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

Normalerweise sollte dies bei den meisten Architekturen bis zu einer einzigen Rotationsanweisung vereinfacht werden (ignorieren, dass die gesamte Operation wahrscheinlich eine Anweisung ist).

Auf einem RISC-Prozessor können die großen, komplizierten Konstanten zu Schwierigkeiten beim Compiler führen. Sie können jede Konstante aus der vorherigen trivial berechnen. So wie:

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

Wenn Sie möchten, können Sie das als Schleife schreiben. Es wird nicht effizient sein, sondern nur zum Spaß:

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

Der Vollständigkeit halber hier die vereinfachte 32-Bit-Version des ersten Formulars:

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

Ich bin wirklich überrascht, dass niemand die Funktionen von htobeXX und betohXX erwähnt hat. Sie sind in endian.h definiert und den Netzwerkfunktionen htonXX sehr ähnlich.

1
Nick

Mit den unten angegebenen Codes können Sie problemlos zwischen Big Endian und Little Endian wechseln 

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

Scheint der sichere Weg zu sein, htons für jedes Wort zu verwenden. Also, wenn Sie haben ...

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

Das obige wäre ein No-Op, wenn Sie auf einem Big-Endian-System wären, also würde ich nach dem suchen, was Ihre Plattform als Kompilierungsbedingung verwendet, um zu entscheiden, ob htons ein No-Op ist. Es ist schließlich O(n). Auf einem Mac wäre es so etwas wie ...

#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

Tragbare Technik zum Implementieren von optimiererfreundlichen, nicht abgeglichenen Endian-Accessoren. Sie arbeiten mit jedem Compiler, jeder Begrenzungsausrichtung und jeder Bytereihenfolge. Diese unausgerichteten Routinen werden je nach nativem Endian und Ausrichtung angepasst oder ergänzt. Teilauflistung, aber Sie bekommen die Idee. BO * sind konstante Werte, die auf der nativen Byte-Reihenfolge basieren.

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

Diese Typedefs haben den Vorteil, dass sie Compiler-Fehler auslösen, wenn sie nicht mit Accessoren verwendet werden. Dadurch werden vergessene Accessor-Fehler verringert.

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

Ich habe kürzlich ein Makro geschrieben, um dies in C zu tun, aber es ist in C++ gleichermaßen gültig:

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

Es akzeptiert jeden Typ und kehrt die Bytes im übergebenen Argument um .. _. Beispielverwendungen:

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

Welche drucke:

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

Das oben Genannte ist perfekt zum Kopieren/Einfügen geeignet, aber hier ist viel los. Ich werde die Funktionsweise Stück für Stück aufteilen:

Der erste bemerkenswerte Punkt ist, dass das gesamte Makro in einem do while(0)-Block eingeschlossen ist. Dies ist eine Common Idiom , die die normale Verwendung von Semikolons nach dem Makro erlaubt.

Als Nächstes wird eine Variable mit dem Namen REVERSE_BYTES als Zähler der for-Schleife verwendet. Der Name des Makros selbst wird als Variablenname verwendet, um sicherzustellen, dass er nicht mit anderen Symbolen kollidiert, die sich im Geltungsbereich des Makros befinden. Da der Name in der Erweiterung des Makros verwendet wird, wird er nicht erneut erweitert, wenn er hier als Variablenname verwendet wird.

Innerhalb der for-Schleife werden zwei Bytes referenziert und XOR ausgetauscht (daher ist kein temporärer Variablenname erforderlich):

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

__VA_ARGS__ steht für alles, was dem Makro gegeben wurde, und wird verwendet, um die Flexibilität der weitergeleiteten Elemente zu erhöhen (wenn auch nicht viel). Die Adresse dieses Arguments wird dann genommen und in einen unsigned char-Zeiger umgewandelt, um das Austauschen seiner Bytes über das Array []-Subskription zu ermöglichen.

Der letzte besondere Punkt ist das Fehlen von {}-Klammern. Sie sind nicht erforderlich, da alle Schritte in jedem Swap mit dem Operator comma verknüpft werden, wodurch sie zu einer einzigen Anweisung werden.

Schließlich ist es erwähnenswert, dass dies nicht der ideale Ansatz ist, wenn Geschwindigkeit oberste Priorität hat. Wenn dies ein wichtiger Faktor ist, sind einige der typspezifischen Makros oder plattformspezifischen Direktiven, auf die in anderen Antworten verwiesen wird, wahrscheinlich die bessere Option. Dieser Ansatz ist jedoch auf alle Arten, alle wichtigen Plattformen und sowohl die C- als auch die C++ - Sprache portierbar.

0
Ryan Hilbert

So lesen Sie ein im IEEE 754 64-Bit-Format gespeichertes Doppel, auch wenn Ihr Host-Computer ein anderes System verwendet.

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

Für den Rest der Suite von Funktionen, einschließlich der Write- und Integer-Routinen, siehe mein Github-Projekt

https://github.com/MalcolmMcLean/ieee754

0
Malcolm McLean

Das Austauschen von Bytes mit der alten 3-Schritt-Methode oder der Trick um einen Pivot in einer Vorlagenfunktion ergibt eine flexible, schnelle O(ln2) Lösung, die keine Bibliothek erfordert. Der Stil lehnt hier auch 1-Byte-Typen ab:

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

Versuchen Sie es mit Boost::endian und setzen Sie es sich nicht selbst um!

Hier ist ein Link

0
morteza

Wow, ich konnte einige der Antworten, die ich hier gelesen habe, nicht glauben. Es gibt tatsächlich eine Anweisung in Assembly, die dies schneller als alles andere macht. bswap. Sie könnten einfach eine Funktion wie diese schreiben ...

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

Es istVIELschneller als die vorgeschlagenen Intrinsics. Ich habe sie demontiert und gesucht. Die obige Funktion hat keinen Prolog/Epilog, also praktisch keinen Overhead.

unsigned long _byteswap_ulong(unsigned long value);

16 Bit ist genauso einfach, mit der Ausnahme, dass Sie xchg al, ah verwenden würden. bswap funktioniert nur bei 32-Bit-Registern.

64-Bit ist etwas schwieriger, aber nicht übertrieben. Viel besser als alle obigen Beispiele mit Schleifen und Vorlagen usw.

Hier gibt es einige Vorbehalte ... Erstens ist bswap nur auf 80x486-CPUs und darüber verfügbar. Plant jemand, es auf einer 386 auszuführen?!? Wenn ja, können Sie bswap immer noch ersetzen durch ...

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

Inline-Assembly ist in Visual Studio auch nur im x86-Code verfügbar. Eine Naked-Funktion kann nicht ausgekleidet werden und ist auch in x64-Builds nicht verfügbar. In diesem Fall müssen Sie die Compiler-Intrinsics verwenden.

0
The Welder