C ++ 'da big-endian ve little-endian değerleri arasında nasıl dönüştürme yapabilirim?


196

C ++ 'da big-endian ve little-endian değerleri arasında nasıl dönüştürme yapabilirim?

EDIT: Netlik için, ikili verileri (çift duyarlıklı kayan nokta değerleri ve 32 bit ve 64 bit tamsayılar) bir CPU mimarisinden diğerine çevirmek zorundayım. Bu ağ oluşturmayı içermez, bu nedenle ntoh () ve benzer işlevler burada çalışmaz.

EDIT # 2: Kabul ettiğim cevap doğrudan hedeflediğim derleyiciler için geçerlidir (bu yüzden seçtim). Ancak, burada başka çok iyi, daha taşınabilir cevaplar var.


21
ntoh hton, ağ ile ilgisi olmasa bile iyi çalışır.
Ben Collins

2
Genel olarak endianness ile başa çıkmanın en iyi yolu, kodun hem küçük hem de büyük endian ana makinelerde çalıştığından emin olmaktır. Bu işe yararsa, muhtemelen doğru yaptınız. X86 / be'de olduğunuzu farz etmek, bir uygulama olarak tehlikelidir.
jakobengblom2

10
hton ntoh makine big-endian ise çalışmaz, çünkü soru asker açıkça dönüşüm gerçekleştirmek istiyor.
fabspro

6
@ jakobengblom2 bundan bahseden tek kişidir. Bu sayfadaki örneklerin neredeyse tamamı, altta yatan endianiteye karşı agnostik yapmak yerine "takas" baytları gibi kavramlar kullanır. Harici dosya formatlarıyla (iyi tanımlanmış bir endianiteye sahip) uğraşıyorsanız, yapılacak en taşınabilir şey harici verileri bir bayt akışı olarak ele almak ve bayt akışını yerel tamsayılara dönüştürmek. short swap(short x)Kod gördüğüm her seferinde küfrediyorum , çünkü farklı endianiteye sahip bir platforma geçerseniz kırılacak. Matthieu M aşağıdaki tek doğru cevaba sahiptir.
Mark Lakata

3
Sorunu tamamen yanlış düşünüyorsun. Görev "big-endian ve little-endian değerleri arasında nasıl dönüştürme yaparım" değildir. Görev "kayan nokta ve tam sayı değerlerini platformumun yerel biçimine nasıl dönüştürebilirim" dir. Doğru yaparsanız, yerel biçim tüm kod bakımlarınız için büyük endian, küçük endian, karışık endian veya üçlü olabilir.
David Schwartz

Yanıtlar:


166

Visual C ++ kullanıyorsanız aşağıdakileri yapın: intrin.h ekleyin ve aşağıdaki işlevleri çağırın:

16 bitlik sayılar için:

unsigned short _byteswap_ushort(unsigned short value);

32 bit sayılar için:

unsigned long _byteswap_ulong(unsigned long value);

64 bit sayılar için:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 bit sayıların (karakter) dönüştürülmesine gerek yoktur.

Ayrıca bunlar yalnızca işaretli tamsayılar için de çalıştıkları imzasız değerler için tanımlanır.

Şamandıralar ve çiftler için, düz tamsayılarda olduğu gibi daha zordur, çünkü bunlar ana makine bayt sırasına göre olabilir veya olmayabilir. Büyük endian makinelerde küçük endian şamandıraları elde edebilirsiniz.

Diğer derleyiciler de benzer özelliklere sahiptir.

Örneğin GCC'de bazı yerleşikleri burada belgelendiği gibi doğrudan çağırabilirsiniz :

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(bir şey eklemenize gerek yoktur). Afaik bits.h aynı işlevi gcc merkezli olmayan bir şekilde de açıklar.

16 bit takas sadece biraz döndür.

Kendinizi yuvarlamak yerine gerçekleri çağırmak size en iyi performansı ve kod yoğunluğunu sağlar.


11
GCC ile şunu kullanabilirim: #include <byteswap.h> int32_t bswap_32 (int32_t x) int64_t bswap_64 (int64_t x)
jmanning2k

5
__builtin_bswapXsadece GCC-4.3'ten itibaren kullanılabilir
Matt Joiner

20
Bu intrinsics / her zaman / takas bayt, değil gibi olduğunu belirterek, aynı zamanda değer htonl, htonsvb Sen bayt aslında takas zaman durumun bağlamdan bilmek zorunda.
Brian Vandenberg

8
@Jason çünkü 8 bit sayıları büyük ve küçük endianlarda aynıdır. :-)
Nils Pipenbrinck

2
@BrianVandenberg Sağ; kullanarak htonlve ntohlçok az / orta endian ve üzerinde ise takas olur bu işlevleri tanımlayan platformu beri taşınabilir kod yazarken çalışacak bağlamda endişesi duymadan big-endian bir no-op olurdu. Biraz endian (BMP demek) olarak tanımlanır standart bir dosya türünü deşifre Ancak, biri hala bağlamı bilmek zorunda değil ve sadece güvenemez htonlve ntohl.
legends2k

86

Basit ifadeyle:

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

kullanımı: swap_endian<uint32_t>(42).


3
Bir oy verin. Ben sadece uchars kullandım ve 4 ila 1, 3 ila 2, 2 ila 3 ve 1 ila 4 atadım, ancak farklı boyutlarınız varsa bu daha esnektir. 1.Nesil Pentium IIRC'de 6 saat. BSWAP 1 saattir, ancak platforma özgüdür.

2
@RocketRoy: Evet, eğer hız bir sorun haline gelirse, platforma ve türe özgü intrisiklerle aşırı yük yazmak çok basittir.
Alexandre

3
@MihaiTodor: Bir dizi karakter aracılığıyla daktilo yapmak için sendikaların bu kullanımına standart olarak açıkça izin verilir. Bkz. bu soru .
Alexandre C.

4
@AlexandreC. C ++ standardında değil - yalnızca C.'de. C ++ 'da (bu kod olduğu) bu kod tanımsız davranıştır.
Rapptz

4
@Rapptz: 3.10 açık görünüyor: "Bir program, bir nesnenin depolanan değerine aşağıdaki türlerden birinden farklı bir değerle erişmeye çalışırsa, davranış tanımsızdır: [...] bir karakter veya işaretsiz karakter türü. ". Belki burada bir şey eksik, ama char işaretçileri aracılığıyla herhangi bir tür erişim açıkça izin verildi benim için oldukça açıktı.
Alexandre

75

Gönderen Bayt al Fallacy Rob Pike tarafından:

Diyelim ki veri akışınızda biraz endian kodlu 32 bit tam sayı var. İşte nasıl ayıklanır (imzasız bayt varsayarak):

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

Big-endian ise, onu nasıl çıkaracağınız aşağıda açıklanmıştır:

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

TL; DR: platform yerel siparişiniz için endişelenmeyin, önemli olan okuduğunuz akışın bayt sırasıdır ve iyi tanımlandığını umarsanız iyi olur.

Not: Yorumda, açık tür dönüşümü olmadığı belirtildi data, unsigned charveya bir dizi olması önemliydi uint8_t. Kullanılması signed charveya char(kaydettiyseniz) sonuçlanacaktır data[x]bir tamsayı terfi ve data[x] << 24potansiyel olarak UB olduğunu işaret biti içine 1 kayması.


6
Bu harika, ama bana göre sadece tamsayılar ve varyantlar için geçerli. Şamandıralar / çiftler ile ne yapmalı?
Brett

1
@ v.oddou: evet ve hayır, bellek eşlemeli dosyalar ağ çerçevelerinden tamamen aynıdır; Eğer kabul ederseniz değil önemli olan tüm bu doğrudan onları okumak için kendi endianness'ın: o büyük-endian ise küçük endian, birinci formülü kullanırsanız, ikinci kullanın. Tuz değerine sahip herhangi bir derleyici, endianness eşleşirse gereksiz dönüşümleri optimize eder.
Matthieu M.

2
@meowsqueak: Evet, çalışmasını beklerim, çünkü her bayt içindeki bitlerin sırası değil, sadece bayt sırası değişir.
Matthieu

3
Gevşeklikle ilgili bir notta, bağlantılı yazı hoş olmayan bir okuma ... Adam kısalık değer veriyor gibi görünüyor, ancak aslında endianizmle ilgili olarak aydınlanmayan tüm kötü programcılar hakkında aslında yerine uzun bir rant yazmayı tercih etti. durumu ve NEDEN çözümü her zaman işe yarıyor.
Reklam N

1
Bu yöntemi kullanıyorsanız, verilerinizi (imzasız karakter *) kullandığınızdan emin olun
joseph

51

Bunu ağ / ana bilgisayar uyumluluğu için yapıyorsanız:

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)

Bunu başka bir nedenden dolayı yapıyorsanız, burada sunulan byte_swap çözümlerinden biri iyi çalışır.


2
ağ bayt sıralaması inanıyorum büyük endian olduğunu. Bu işlevler, ağ kodu kullanmasanız bile, bunu göz önünde bulundurarak kullanılabilir. Ancak ntohf veya htonf şamandıra versiyonu yok
Matt

2
Matt H. sadece doğru. Tüm bilgisayar sistemlerinin çok az endian bayt sırası yoktur. Bir motorolla 68k, bir PowerPC veya başka bir büyük endian mimarisi üzerinde çalışıyorsanız, bu işlevler zaten baytları değiştirmeyecektir, çünkü zaten 'Ağ bayt sırası'ndadırlar.
Soğuk

2
Ne yazık ki, htonlve ntohlbüyük-endian platformda küçük endian gidemez.
Brian Vandenberg

2
@celtschk, anlaşıldı; bununla birlikte OP, büyük endian bir ortamda bile endianizmi değiştirmenin bir yolunu ister.
Brian Vandenberg

4
Kaçınılmaz soruyu ortadan kaldırmak için: BE platformu için LE'ye ihtiyaç duymanın birkaç nedeni vardır; bir dizi dosya formatı (bmp, fli, pcx, qtm, rtf, tga birkaçını belirtmek için) küçük endian değerleri kullanır ... ya da en azından formatın bazı versiyonları yine de bir kerede yaptı.
Brian Vandenberg

26

Bu yazıdan birkaç öneri aldım ve bunları oluşturmak için bir araya getirdim:

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

Ayrıca, örneğin, uint32_t
ady

17

Big-endian'dan little-endian'a gitme prosedürü, küçük-endiandan big-endian'a gitmeyle aynıdır.

İşte bazı örnek kod:

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

2
Burada gönderilen son işlev yanlıştır ve şu şekilde düzenlenmelidir: void swapByteOrder (unsigned long long & ull) {ull = (ull >> 56) | ... (ull << 56); }
Eric Burnett

14
Mantıksal-ve (&&) bitsel yerine- ve (&) kullanmanın doğru olduğunu düşünmüyorum. C ++ spesifikasyonuna göre, her iki işlenen de dolaylı olarak boole dönüştürülür, bu da istediğiniz şey değildir.
Trevor Robinson

15

Son derece hızlı bir şekilde takas yapacağınız BSWAP adlı bir montaj talimatı var . Burada okuyabilirsiniz .

Visual Studio veya daha doğrusu Visual C ++ çalışma zamanı kitaplığı, bunun için platform özüne sahiptir _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Diğer platformlar için de benzer bir şey olmalı, ama ne deneceklerinin farkında değilim.


Bu harika bir bağlantı. X86 montajcısına olan ilgimi tekrar canlandırdı.
PP.

1
BSWAP için zamanlama sonuçları burada sunulmaktadır. gmplib.org/~tege/x86-timing.pdf ... ve burada ... agner.org/optimize/instruction_tables.pdf

12

Bunu şablonlarla yaptık. Böyle bir şey yapabilirsiniz:

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

8

Bunu farklı platformlar arasında veri aktarmak için yapıyorsanız, ntoh ve hton işlevlerine bakın.


7

C ile aynı şekilde:

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

Ayrıca, imzalanmamış karakterlerin bir vektörünü bildirebilir, giriş değerini buna memcpy edebilir, baytları başka bir vektöre tersine çevirebilir ve baytları memcpy edebilirsiniz, ancak bu, özellikle 64 bit değerlerle, bit döndürmekten daha uzun büyüklük dereceleri alacaktır.


7

Çoğu POSIX sisteminde (POSIX standardında olmadığı için), sisteminizin hangi kodlamayı kullandığını belirlemek için kullanılabilen endian.h vardır. Oradan böyle bir şey:

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

Bu emri değiştirir (büyük endiandan küçük endiana):

0xDEADBEEF numaranız varsa (0xEFBEADDE olarak depolanan küçük bir endian sisteminde), ptr [0] 0xEF, ptr [1] 0xBE vb. Olacaktır.

Ancak ağ için kullanmak istiyorsanız, htons, htonl ve htonll (ve tersleri ntohs, ntohl ve ntohll), ana bilgisayar siparişinden ağ siparişine dönüştürmek için yardımcı olacaktır.


7
Bu komik - opengroup.org/onlinepubs/9699919799/toc.htm adresindeki POSIX standardı '<endian.h> `başlıklarından bahsetmiyor.
Jonathan Leffler

1
Kullanım htonlsenaryosunun ağ ile ilgili olup olmadığına bakılmaksızın ve arkadaşlarınızı kullanabilirsiniz. Ağ bayt sırası big-endian olduğundan, bu işlevleri host_to_be ve be_to_host olarak ele almanız yeterlidir. (Host_to_le'a ihtiyacınız varsa yardımcı olmaz.)
Peter Cordes

5

En azından Windows için, htonl () işlevinin kendi muadili _byteswap_ulong () 'dan çok daha yavaş olduğunu unutmayın. Birincisi ws2_32.dll içine DLL kütüphanesi çağrısı, ikincisi bir BSWAP montaj talimatıdır. Bu nedenle, platforma bağlı bir kod yazıyorsanız, hız için içselleri kullanmayı tercih edin:

#define htonl(x) _byteswap_ulong(x)

Bu, tüm tamsayıların Big Endian'a kaydedildiği .PNG görüntü işleme için, "Hazır değilseniz, tipik Windows programlarını yavaşlatmak için" Biri htonl () ... "kullanabilirsiniz.


4

Çoğu platformda verimli byteswap işlevleri sağlayan bir sistem başlık dosyası vardır. Linux'ta öyle <endian.h>. C ++ 'da güzelce sarabilirsiniz:

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

Çıktı:

cafe
deadbeaf
feeddeafbeefcafe

Değiştir: #define BYTESWAPS (bit) \ şablon <sınıf T> satır içi T htobe (T t, SizeT <bit / 8>) {dönüş htobe ## bit (t); } \ şablon <sınıf T> satır içi T htole (T t, SizeT <bit / 8>) {dönüş htole ## bit (t); } \ template <class T> satır içi T betoh (T t, SizeT <bit / 8>) {dönüş ## bit ## toh (t); } \ şablon <sınıf T> satır içi T letoh (T t, SizeT <bit / 8>) {dönüş le ## bit ## toh (t); }
ldav1s

Teşekkürler, betoh () ve letoh () test etmeyi unuttum.
Maxim Egorushkin

4

Bunu seviyorum, sadece stil için :-)

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

char[]'Hata: eksik türe izin verilmiyor' hatasıyla ilgili bir hata alıyorum
Portland Runner

4

Cidden ... Tüm çözümlerin neden bu kadar karmaşık olduğunu anlamıyorum ! Herhangi bir işletim sisteminde herhangi bir koşulda herhangi bir boyutta herhangi bir türü değiştiren en basit, en genel şablon işlevine ne dersiniz ????

template <typename T>
void SwapEnd(T& var)
{
    static_assert(std::is_pod<T>::value, "Type must be POD type for safety");
    std::array<char, sizeof(T)> varArray;
    std::memcpy(varArray.data(), &var, sizeof(T));
    for(int i = 0; i < static_cast<int>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
    std::memcpy(&var, varArray.data(), sizeof(T));
}

Birlikte C ve C ++ 'nın sihirli gücü! Sadece orijinal değişken karakter karakter değiş tokuş.

1. Nokta : İşleç yok: Basit atama işleci "=" kullanmadığımı unutmayın, çünkü endianness çevrildiğinde ve kopya oluşturucu (veya atama operatörü) çalışmadığında bazı nesneler karıştırılacaktır. Bu nedenle, char ile char kopyalamak daha güvenilirdir.

2. Nokta : Hizalama sorunlarının farkında olun: C ++ derleyicisi, hizalanmamış belleğe erişebileceğimizi garanti etmediği için, bir diziye ve diziden kopyaladığımızın farkına varın (bu yanıt orijinalinden güncellendi) bunun için form). Örneğin, ayırırsanız uint64_t, derleyiciniz bunun 3. baytına a olarak erişebileceğinizi garanti edemez uint8_t. Bu nedenle, yapılacak doğru şey bunu bir char dizisine kopyalamak, değiştirmek ve sonra tekrar kopyalamaktır (yani hayır reinterpret_cast). Derleyicilerin, reinterpret_casthizalamadan bağımsız olarak tek tek baytlara erişebilmeleri durumunda, yaptığınız şeyi bire dönüştürecek kadar akıllı olduğuna dikkat edin .

Bu işlevi kullanmak için :

double x = 5;
SwapEnd(x);

ve şimdi xendianizmde farklı.


2
Bu her yerde çalışacaktır, ancak üretilen montaj okde çoğu zaman yetersiz olacaktır: soruma bakın stackoverflow.com/questions/36657895/…
j_kubik

Bunun için bir arabellek ayırmak için new/ deletekullanın. sizeof(var)derleme zamanı sabiti, böylece yapabilirsiniz char varSwapped[sizeof(var)]. Veya char *p = reinterpret_cast<char*>(&var)yerinde yapabilir ve değiştirebilirsiniz.
Peter Cordes

@Peter bu cevabı hızlı ve kirli bir nokta kanıtlamak için yapılır. Ben önerilerinizi uygulayacağım. Bununla birlikte, mega SO AH olmanıza ve 5 satırlı çözümü, orada verilen 50 satırlı çözümlere kıyasla daha düşük oylamaya ihtiyacınız yok. Daha fazlasını söylemeyeceğim.
Kuantum Fizikçisi

Bu cevap, yanlış endian verilerde kuruculara ve aşırı yüklenen operatörlere dikkat etme konusunda bazı yararlı noktalar yapar, bu nedenle kod korkunç olmadığında ve iyi bir derleyicinin bswap'a derleyebileceği bir şey olduğunu düşünürüm. talimat. Ayrıca, for(size_t i = 0 ; i < sizeof(var) ; i++)a yerine kullanmanızı öneririm static_cast<long>. (Veya aslında, yerinde takas, artan ve azalan birchar* şekilde böylece yine de gider).
Peter Cordes

örneğin Mark Ransom'un yerinde ters çevirmek için std :: swap kullanarak cevabına bakınız .
Peter Cordes

3

HOST_ENDIAN_ORDER (ne olursa olsun) LITTLE_ENDIAN_ORDER veya BIG_ENDIAN_ORDER dönüştürmek için izin veren bu kod var. Bir şablon kullanıyorum, bu yüzden HOST_ENDIAN_ORDER'dan LITTLE_ENDIAN_ORDER'a dönüştürmeye çalışırsam ve derlediğim makine için aynı olurlarsa, kod üretilmez.

İşte bazı yorumlarla kod:

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

Büyük bir endian 32 bit işaretsiz tam sayı, 2864434397'ye eşit olan 0xAABBCCDD'ye benziyorsa, aynı 32 bit işaretsiz tam sayı, 2864434397'ye eşit olan küçük bir endian işlemcide 0xDDCCBBAA gibi görünür.

Büyük bir endian 16 bit imzasız kısa, 43707'ye eşit olan 0xAABB gibi görünüyorsa, aynı 16 bit imzasız kısa, aynı zamanda 43707'ye eşit olan küçük bir endian işlemcide 0xBBAA'ya benziyor.

Baytları küçük endiandan big endian'a ve tersini değiştirmek için birkaç kullanışlı #define işlevi vardır ->

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

2

İşte bir değeri yerine koymak için başımın üstünden çıkardığım genelleştirilmiş bir sürüm. Performans bir sorunsa diğer öneriler daha iyi olurdu.

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

Feragatname: Bunu derlemeye ya da henüz test etmeye çalışmadım.


2

Bir sözcükteki bitlerin sırasını tersine çevirmek için ortak kalıbı alır ve her bayt içindeki bitleri tersine çeviren kısmı kaldırırsanız, yalnızca bir sözcük içindeki baytları tersine çeviren bir şey kalır. 64 bit için:

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

Derleyici gerekir gereksiz bit maskeleme işlemleri (I desen vurgulamak için bunları bıraktı) temizler ancak öyle değilse ilk satırını bu şekilde yazabilirsiniz:

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

Bu normalde çoğu mimaride tek bir döndürme komutuna basitleştirmelidir (tüm işlemin büyük olasılıkla bir komut olduğunu göz ardı ederek).

RISC işlemcide büyük, karmaşık sabitler derleyici zorluklarına neden olabilir. Yine de, öncekinden sabitlerin her birini önemsiz bir şekilde hesaplayabilirsiniz. Şöyle ki:

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

İsterseniz, bunu bir döngü olarak yazabilirsiniz. Etkili olmayacak, sadece eğlence için:

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

Ve tamlık için, ilk formun basitleştirilmiş 32 bit sürümü:

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

2

Sadece kendi çözümümü buraya eklediğimi düşündüm çünkü hiçbir yerde görmedim. Sadece bit işlemlerini kullanan küçük ve taşınabilir bir C ++ şablonlu işlevdir ve taşınabilirdir.

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

Kimse htobeXX ve betohXX fonksiyonları bahsetmedi gerçekten şaşırdım. Bunlar endian.h dosyasında tanımlanmıştır ve htonXX ağ işlevlerine çok benzer.


2

Aşağıdaki kodları kullanarak BigEndian ve LittleEndian arasında kolayca geçiş yapabilirsiniz

#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

Son zamanlarda C bunu yapmak için bir makro yazdım, ama C ++ eşit derecede geçerli:

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

Herhangi bir türü kabul eder ve iletilen argümandaki baytları tersine çevirir. Örnek kullanımlar:

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

Hangi baskılar:

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

Yukarıdakiler mükemmel bir şekilde kopyalayıp yapıştırabilir, ancak burada çok şey oluyor, bu yüzden parça parça nasıl çalıştığını parçalayacağım:

Dikkate değer ilk şey, tüm makronun bir do while(0)blok içine yerleştirilmiş olmasıdır . Bu yaygın bir deyim makrodan sonra normal noktalı virgül kullanımına izin veren .

Bir sonraki adım, döngü sayacı REVERSE_BYTESolarak adlandırılan bir değişkenin kullanılmasıdır for. Makronun adı, makronun kullanıldığı her yerde kapsamda olabilecek herhangi bir sembolle çakışmamasını sağlamak için değişken adı olarak kullanılır. Ad, makronun genişletilmesinde kullanıldığından, burada değişken adı olarak kullanıldığında yeniden genişletilmez.

forDöngü içinde, iki bayt başvuruda bulunur ve XOR değiştirilir (bu nedenle geçici bir değişken adı gerekli değildir):

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

__VA_ARGS__makroya verilen her şeyi temsil eder ve aktarılabileceklerin esnekliğini arttırmak için kullanılır (çok fazla olmasa da). Bu argümanın adresi daha sonra alınır ve unsigned charbaytlarının dizi yoluyla değiştirilmesine izin vermek için bir işaretçiye gönderilir[] aboneliği .

Son tuhaf nokta, {}diş teli eksikliğidir . Gerekli değildir, çünkü her takastaki tüm adımlar virgül operatörü ile birleştirilir ve bu da onları bir ifade haline getirir.

Son olarak, hızın öncelikli olduğu durumlarda bunun ideal bir yaklaşım olmadığını belirtmek gerekir. Bu önemli bir faktörse, diğer yanıtlarda belirtilen türe özgü makroların veya platforma özgü yönergelerin bazıları muhtemelen daha iyi bir seçenektir. Ancak bu yaklaşım tüm türlere, tüm büyük platformlara ve hem C hem de C ++ dillerine taşınabilir.


bazı kodlarda bir yerde buldum. kafamı karıştırdı. Açıklama için teşekkürler. Ancak neden kullanımı __VA_ARGS__?
asr9

0

Vay be, burada okuduğum bazı cevaplara inanamadım. Aslında montajda bunu her şeyden daha hızlı yapan bir talimat var. bswap. Bunun gibi bir işlev yazabilirsiniz ...

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

Öyle FAZLA hızlı ileri sürülmüştür intrinsics daha. Onları söküp baktım. Yukarıdaki fonksiyonun prolog / epilogu yoktur, bu yüzden neredeyse hiç ek yükü yoktur.

unsigned long _byteswap_ulong(unsigned long value);

16 bit yapmak, xchg al, ah kullanmak dışında bir o kadar kolaydır. bswap yalnızca 32 bit kayıtlarda çalışır.

64-bit biraz daha zor, ama aşırı değil. Döngüler ve şablonlar vb. İle yukarıdaki örneklerin hepsinden çok daha iyi.

Burada bazı uyarılar var ... Öncelikle bswap sadece 80x486 CPU ve üstü modellerde mevcut. 386 üzerinde çalıştırmayı planlayan var mı?!? Öyleyse, bswap'ı yine de ...

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

Ayrıca satır içi derleme yalnızca Visual Studio'da x86 kodunda kullanılabilir. Çıplak bir işlev astarlanamaz ve ayrıca x64 sürümlerinde kullanılamaz. Bu örnekte, derleyici intrinsiklerini kullanmanız gerekecek.


1
_byteswap_ulongve _uint64(örneğin kabul edilen cevapta) her ikisi de bswaptalimatı kullanmak için derlenir. Şaşırırdım ama bu grubun sadece prolog / epiloğu atladığı kadar hızlı olup olmadığını bilmekle ilgileniyorum - kıyasladınız mı?
ZachB

@stdcall Soru taşınabilir bir çözüm istemedi ve hatta bir platform hakkında hiçbir şey söylemedi. Cevabımın söylediği gibi, yukarıdaki endian takasının en hızlı yolu hakkında. Elbette, bunu X86 olmayan bir platformda yazıyorsanız, bu işe yaramayacaktır, ancak daha önce de belirttiğim gibi, derleyiciniz bile destekliyorsa, derleyici içselleriyle sınırlısınız.
Kaynakçı

@ZachB Bu özel durumda, bence prolog ve epilog atlamak size iyi bir tasarruf sağlayacak çünkü aslında sadece 1 talimat yürütüyorsunuz. Prolog yığının üzerine itmek, bir çıkarma yapmak, taban imlecini ayarlamak ve sonunda benzer olacak. Ben karşılaştırmak değil, ama yukarıdaki 0 ​​bağımlılık zinciri vardır ki sadece çıplak olmadan almak olmaz. Belki iyi bir derleyici satır içi olurdu, ama sonra farklı bir top parkındasınız.
Kaynakçı

2
Belki. Ancak, bir dizi sayıyı değiştirmenin yaygın durumunda, diğer cevaplarda tartışılan derleyici içsellerinin SSE / AVX uzantılarını kullanacağını ve BSWAP'den daha iyi performans gösteren PSHUFB yayınlayacağını unutmayın. Bkz wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB

OP yalnızca x86 için bir çözüme ihtiyaç duyduklarını belirtmediğinde, platforma özgü bir çözüm yayınlamak IMHO'nun kötü bir biçimidir. Ve diğer çözümleri küçümsemek için, sizinki iOS ve Android gibi (ARM veya MIPS CPU'ları kullanan) çok yaygın olarak kullanılan birçok işletim sisteminde kullanılamaz olduğunda
Jens Alfke

0

Optimize edici dostu hizalanmamış yerinde olmayan endian erişimcilerini uygulamak için taşınabilir teknik. Her derleyici, her sınır hizalaması ve her bayt sırası üzerinde çalışırlar. Bu hizalanmamış rutinler, doğal endian ve hizalamaya bağlı olarak tamamlanır veya bağlanır. Kısmi listeleme ama fikir olsun. BO *, yerel bayt sırasına dayalı sabit değerlerdir.

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

Bu tip tanımlayıcılar, erişimcilerle birlikte kullanılmazsa derleyici hatalarını artırma ve böylece unutulmuş erişimci hatalarını azaltma avantajına sahiptir.

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

2
Bu soru için, C ++ etiketi bir fark yaratır. C ++ ve birleşim nedeniyle tanımlanmamış birçok davranış var.
jww

0

Ana bilgisayarınız farklı bir sistem kullanıyor olsa bile, IEEE 754 64 bit biçiminde depolanan bir çiftin nasıl okunacağı aşağıda açıklanmıştır.

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

Yazma ve tamsayı rutinler dahil olmak üzere diğer işlevler için github projeme bakın

https://github.com/MalcolmMcLean/ieee754


0

Şablon işlevinde bir pivot etrafında 3 adım-xor hile ile bayt değişimi, kütüphane gerektirmeyen esnek, hızlı bir O (ln2) çözümü verir, buradaki stil ayrıca 1 bayt türlerini de reddeder:

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

Güvenli bir şekilde her kelimede hton kullanmak gibi görünüyor. Yani, eğer varsa ...

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

Eğer büyük-endian bir sistemde olsaydınız yukarıdakiler hayır olurdu, bu yüzden platformunuzun derleme zamanı koşulu olarak kullandığı her şeyi htons'un no-op olup olmadığına karar vermek için ararım. Sonuçta O (n) 'dir. Mac bilgisayarlarda ...

#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

C ++ 17'niz varsa bu başlığı ekleyin

#include <algorithm>

Baytları değiştirmek için bu şablon işlevini kullanın:

template <typename T>
void swapEndian(T& buffer)
{
    static_assert(std::is_pod<T>::value, "swapEndian support POD type only");
    char* startIndex = static_cast<char*>((void*)buffer.data());
    char* endIndex = startIndex + sizeof(buffer);
    std::reverse(startIndex, endIndex);
}

şöyle çağır:

swapEndian (stlContainer);

-4

Biraz kaymaya bakın, bu temel olarak küçük -> büyük endiandan takas etmek için yapmanız gereken tek şey. Daha sonra bit boyutuna bağlı olarak, bit kaydırmayı nasıl yapacağınızı değiştirirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.