Bagaimana cara mengkonversi antara nilai big-endian dan little-endian di C ++?

196

Bagaimana cara mengkonversi antara nilai big-endian dan little-endian di C ++?

EDIT: Untuk lebih jelasnya, saya harus menerjemahkan data biner (nilai floating point presisi ganda dan integer 32-bit dan 64-bit) dari satu arsitektur CPU ke arsitektur CPU lainnya. Ini tidak melibatkan jaringan, jadi ntoh () dan fungsi serupa tidak akan berfungsi di sini.

EDIT # 2: Jawaban yang saya terima berlaku langsung ke kompiler yang saya targetkan (itulah sebabnya saya memilihnya). Namun, ada jawaban lain yang sangat bagus dan lebih portabel di sini.

Uhall
sumber
21
ntoh hton akan berfungsi dengan baik, bahkan jika itu tidak ada hubungannya dengan jaringan.
Ben Collins
2
Cara terbaik untuk menangani endianness secara umum adalah memastikan bahwa kode berjalan pada mesin host kecil dan besar. Jika itu berhasil, Anda mungkin melakukannya dengan benar. Anggap Anda menggunakan x86 / be berbahaya sebagai praktik.
jakobengblom2
10
hton ntoh tidak akan berfungsi jika mesin adalah big-endian, karena penanya pertanyaan secara eksplisit ingin melakukan konversi.
fabspro
6
@ jakobengblom2 adalah satu-satunya orang yang menyebutkan ini. Hampir semua contoh di halaman ini menggunakan konsep seperti "swap" byte alih-alih melakukan agnostik dari endianness yang mendasarinya. Jika Anda berurusan dengan format file eksternal (yang memiliki endianness yang terdefinisi dengan baik) maka hal yang paling portabel untuk dilakukan adalah memperlakukan data eksternal sebagai aliran byte, dan mengonversi aliran byte ke dan dari bilangan bulat asli. Saya merasa ngeri setiap kali saya melihat short swap(short x)kode, karena itu akan rusak jika Anda pindah ke platform dengan endianness yang berbeda. Matthieu M memiliki satu-satunya jawaban yang benar di bawah ini.
Mark Lakata
3
Anda berpikir tentang masalah yang sama sekali salah. Tugasnya bukan "bagaimana saya mengkonversi antara nilai big-endian dan little-endian". Tugasnya adalah "bagaimana cara mengkonversi nilai floating point dan integer dalam format tertentu ke format asli platform saya". Jika Anda melakukannya dengan benar, format asli dapat berupa big endian, little endian, mixed endian, atau ternary untuk semua kode Anda.
David Schwartz

Jawaban:

166

Jika Anda menggunakan Visual C ++ lakukan hal berikut: Anda memasukkan intrin.h dan memanggil fungsi-fungsi berikut:

Untuk angka 16 bit:

unsigned short _byteswap_ushort(unsigned short value);

Untuk nomor 32 bit:

unsigned long _byteswap_ulong(unsigned long value);

Untuk nomor 64 bit:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

Nomor 8 bit (karakter) tidak perlu dikonversi.

Juga ini hanya ditentukan untuk nilai yang tidak ditandatangani yang berfungsi untuk bilangan bulat yang ditandatangani juga.

Untuk mengapung dan menggandakan itu lebih sulit karena dengan bilangan bulat biasa karena ini mungkin atau tidak mungkin ada di byte-order mesin host. Anda bisa mendapatkan pelampung little-endian pada mesin big-endian dan sebaliknya.

Kompiler lain juga memiliki intrinsik yang serupa.

Dalam GCC misalnya, Anda dapat langsung memanggil beberapa builtin seperti yang didokumentasikan di sini :

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

(tidak perlu memasukkan sesuatu). Afaik bits.h mendeklarasikan fungsi yang sama dengan cara non-gcc-centric juga.

16 bit swap itu hanya sedikit-putar.

Memanggil intrinsik alih-alih menggulirkan sendiri memberi Anda kinerja terbaik dan densitas kode antara ..

Nils Pipenbrinck
sumber
11
Dengan GCC, saya dapat menggunakan: #include <byteswap.h> int32_t bswap_32 (int32_t x) int64_t bswap_64 (int64_t x)
jmanning2k
5
__builtin_bswapXhanya tersedia dari GCC-4.3 dan seterusnya
Matt Joiner
20
Ini juga diperhatikan bahwa intrinsik ini / selalu / Swap byte, mereka tidak seperti htonl, htons, dll Anda harus tahu dari konteks situasi Anda saat untuk benar-benar menukar byte.
Brian Vandenberg
8
@ Alasan karena angka 8 bit adalah sama di endian besar dan kecil. :-)
Nils Pipenbrinck
2
@BrianVandenberg Kanan; menggunakan htonldan ntohltanpa khawatir tentang konteks akan bekerja ketika menulis kode portabel karena platform mendefinisikan fungsi-fungsi ini akan menukar itu jika itu sedikit / mid-endian dan pada big-endian itu akan menjadi no-op. Namun, ketika decoding tipe file standar yang didefinisikan sebagai little-endian (katakanlah BMP), kita masih harus tahu konteksnya dan tidak bisa hanya mengandalkan htonldan ntohl.
legends2k
86

Sederhananya:

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

penggunaan: swap_endian<uint32_t>(42).

Alexandre C.
sumber
3
Dapatkan upvote. Saya baru saja menggunakan uchar, dan ditugaskan 4 ke 1, 3 ke 2, 2 ke 3, dan 1 ke 4, tetapi ini lebih fleksibel jika Anda memiliki ukuran yang berbeda. 6 jam pada Pentium IIRC Generasi 1. BSWAP adalah 1 jam, tetapi khusus untuk platform.
2
@RocketRoy: Ya, dan jika kecepatan ternyata menjadi masalah, sangat mudah untuk menulis overload dengan platform dan tipe tertentu.
Alexandre C.
3
@MihaiTodor: Penggunaan serikat pekerja untuk typecasting melalui array karakter secara eksplisit diizinkan oleh standar. Lihat misalnya. pertanyaan ini .
Alexandre C.
4
@AlexandreC. Tidak dalam standar C ++ - hanya dalam C. Dalam C ++ (yang mana kode ini) kode ini adalah perilaku yang tidak terdefinisi.
Rapptz
4
@Rapptz: 3.10 tampak jelas: "Jika suatu program mencoba mengakses nilai yang tersimpan dari suatu objek melalui nilai lain selain salah satu dari jenis berikut perilaku tidak terdefinisi: [...] tipe char atau unsigned char. ". Mungkin saya kehilangan sesuatu di sini, tetapi cukup jelas bagi saya bahwa mengakses semua jenis melalui pointer char diizinkan secara eksplisit.
Alexandre C.
75

Dari The Byte Order Fallacy oleh Rob Pike:

Katakanlah aliran data Anda memiliki integer 32-bit enkode-endian kecil. Berikut cara mengekstraknya (dengan asumsi byte yang tidak ditandatangani):

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

Jika big-endian, inilah cara mengekstraknya:

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

TL; DR: jangan khawatir tentang tatanan asli platform Anda, yang terpenting adalah urutan byte dari aliran yang Anda baca, dan Anda lebih baik berharap itu didefinisikan dengan baik.

Catatan: ada komentar di komentar bahwa tidak ada konversi tipe eksplisit, penting untuk datamenjadi array unsigned charatau uint8_t. Menggunakan signed charatau char(jika ditandatangani) akan data[x]dipromosikan menjadi bilangan bulat dan data[x] << 24berpotensi memindahkan 1 ke bit tanda yang merupakan UB.

Matthieu M.
sumber
6
Ini keren, tetapi bagi saya itu hanya berlaku untuk bilangan bulat dan varian. Apa yang harus dilakukan dengan pelampung / ganda?
Brett
1
@ v.oddou: ya dan tidak, file yang dipetakan memori persis sama dengan bingkai jaringan; jika Anda menerima untuk tidak membacanya secara langsung, yang penting adalah endianness mereka : jika little-endian, gunakan rumus pertama, jika itu big-endian, gunakan yang kedua. Setiap kompiler yang bernilai garamnya akan mengoptimalkan-transformasi yang tidak dibutuhkan jika endianness cocok.
Matthieu M.
2
@mequequeak: Ya, saya berharap itu berfungsi, karena hanya urutan byte yang berubah, bukan urutan bit dalam setiap byte.
Matthieu M.
3
Pada catatan yang terkait longgar, posting terkait adalah beberapa membaca tidak menyenangkan ... Pria itu tampaknya menghargai singkatnya, namun dia lebih suka menulis kata-kata kasar panjang tentang semua programmer buruk yang tidak tercerahkan seperti yang dia tentang endianness, bukannya sebenarnya menjelaskan situasi dan MENGAPA solusinya selalu berhasil.
Ad N
1
Jika Anda menggunakan metode ini, pastikan Anda memasukkan data Anda ke (unsigned char *)
joseph
51

Jika Anda melakukan ini untuk keperluan kompatibilitas jaringan / host Anda harus menggunakan:

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)

Jika Anda melakukan ini karena alasan lain, salah satu solusi byte_swap yang disajikan di sini akan berfungsi dengan baik.

Frosty
sumber
2
pemesanan byte jaringan adalah big endian saya percaya. Fungsi-fungsi ini dapat digunakan dengan itu dalam pikiran bahkan jika Anda tidak menggunakan kode jaringan. Namun tidak ada versi float ntohf atau htonf
Matt
2
Matt H. itu hanya sebagian besar benar. Tidak semua sistem komputer memiliki urutan byte little-endian. Jika Anda sedang mengerjakan, katakanlah motorolla 68k, PowerPC, atau arsitektur big-endian lain fungsi-fungsi ini tidak akan menukar byte sama sekali karena mereka sudah dalam urutan byte jaringan.
Frosty
2
Sayangnya, htonldan ntohltidak bisa pergi ke endian kecil di platform big-endian.
Brian Vandenberg
2
@celtschk, dimengerti; Namun, OP menginginkan cara untuk mengubah endianness, bahkan di lingkungan big-endian.
Brian Vandenberg
4
Untuk menghindari pertanyaan yang tak terhindarkan: ada sejumlah alasan untuk membutuhkan LE untuk platform BE; sejumlah format file (bmp, fli, pcx, qtm, rtf, tga untuk beberapa nama) menggunakan nilai endian kecil ... atau setidaknya, beberapa versi format melakukannya sekaligus.
Brian Vandenberg
26

Saya mengambil beberapa saran dari pos ini dan menyatukannya untuk membentuk ini:

#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);
}
Steve Lorimer
sumber
Anda juga harus memasukkan <cstdint> atau <stdint.h>, misalnya, untuk uint32_t
ady
17

Prosedur untuk beralih dari big-endian ke little-endian sama dengan beralih dari little-endian ke big-endian.

Berikut beberapa contoh kode:

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);
}
Kevin
sumber
2
Fungsi terakhir yang diposting di sini salah, dan harus diedit ke: void swapByteOrder (unsigned long long & ull) {ull = (ull >> 56) | ... (ull << 56); }
Eric Burnett
14
Saya tidak berpikir itu benar untuk menggunakan logical-and (&&) sebagai kebalikan dari bitwise-and (&). Menurut spesifikasi C ++, kedua operan secara implisit dikonversi menjadi bool, yang bukan yang Anda inginkan.
Trevor Robinson
16

Ada instruksi perakitan yang disebut BSWAP yang akan melakukan swap untuk Anda, sangat cepat . Anda dapat membacanya di sini .

Visual Studio, atau lebih tepatnya perpustakaan runtime Visual C ++, memiliki platform intrinsik untuk ini, yang disebut _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Serupa harus ada untuk platform lain, tapi saya tidak tahu apa yang akan mereka sebut.

anon6439
sumber
Itu tautan yang bagus. Ini menyalakan kembali minat saya pada x86 assembler.
PP.
1
Hasil pengaturan waktu untuk BSWAP disajikan di sini. gmplib.org/~tege/x86-timing.pdf ... dan di sini ... agner.org/optimize/instruction_tables.pdf
12

Kami telah melakukan ini dengan templat. Anda dapat melakukan sesuatu seperti ini:

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

Jika Anda melakukan ini untuk mentransfer data antar platform yang berbeda, lihat fungsi ntoh dan hton.

Andrew
sumber
7

Cara yang sama Anda lakukan di C:

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

Anda juga bisa mendeklarasikan vektor karakter yang tidak ditandatangani, memcpy nilai input ke dalamnya, membalikkan byte ke vektor lain dan memcpy byte keluar, tetapi itu akan membuat urutan besarnya lebih lama daripada memutar-mutar bit, terutama dengan nilai 64-bit.

Ben Straub
sumber
7

Pada sebagian besar sistem POSIX (melalui itu tidak dalam standar POSIX) ada endian.h, yang dapat digunakan untuk menentukan pengkodean apa yang digunakan sistem Anda. Dari sana ada sesuatu seperti ini:

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

Ini menukar urutan (dari big endian ke little endian):

Jika Anda memiliki angka 0xDEADBEEF (pada sistem endian kecil yang disimpan sebagai 0xEFBEADDE), ptr [0] akan menjadi 0xEF, ptr [1] adalah 0xBE, dll.

Tetapi jika Anda ingin menggunakannya untuk jaringan, maka htons, htonl dan htonll (dan ntohs terbalik, ntohl dan ntohll) akan membantu untuk mengubah dari pesanan host ke pesanan jaringan.

ujung
sumber
6
Itu lucu - standar POSIX di opengroup.org/onlinepubs/9699919799/toc.htm tidak menyebutkan header '<endian.h> `.
Jonathan Leffler
1
Anda dapat menggunakan htonldan berteman terlepas dari apakah kasus penggunaan itu ada hubungannya dengan jaringan. Urutan byte jaringan adalah big-endian, jadi anggap saja fungsi-fungsi tersebut sebagai host_to_be dan be_to_host. (Namun, tidak membantu jika Anda memerlukan host_to_le.)
Peter Cordes
5

Perhatikan bahwa, setidaknya untuk Windows, htonl () jauh lebih lambat daripada rekan intrinsiknya _byteswap_ulong (). Yang pertama adalah panggilan pustaka DLL ke ws2_32.dll, yang terakhir adalah satu instruksi perakitan BSWAP. Oleh karena itu, jika Anda menulis beberapa kode yang bergantung pada platform, lebih baik menggunakan intrinsik untuk kecepatan:

#define htonl(x) _byteswap_ulong(x)

Ini mungkin sangat penting untuk pemrosesan gambar .PNG di mana semua bilangan bulat disimpan di Big Endian dengan penjelasan "Orang dapat menggunakan htonl () ..." {untuk memperlambat program Windows, jika Anda tidak siap}.

pengguna2699548
sumber
4

Sebagian besar platform memiliki file header sistem yang menyediakan fungsi byteswap yang efisien. Di Linux ada di <endian.h>. Anda dapat membungkusnya dengan baik di 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';
}

Keluaran:

cafe
deadbeaf
feeddeafbeefcafe
Maxim Egorushkin
sumber
Ubah: #define BYTESWAPS (bits) \ template <class T> inline T htobe (Tt, SizeT <bits / 8>) {return htobe ## bits (t); } \ templat <class T> inline T htole (Tt, SizeT <bits / 8>) {return htole ## bits (t); } \ templat <class T> inline T betoh (Tt, SizeT <bits / 8>) {return menjadi ## bits ## toh (t); } \ template <class T> inline T letoh (Tt, SizeT <bits / 8>) {return le ## bits ## toh (t); }
ldav1s
Terima kasih, lupa untuk menguji betoh () dan letoh ().
Maxim Egorushkin
4

saya suka yang ini, hanya untuk gaya :-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
friedemann
sumber
Saya mendapatkan pesan kesalahan saat char[]mengatakan 'Kesalahan: jenis tidak lengkap tidak diizinkan'
Portland Runner
4

Serius ... Saya tidak mengerti mengapa semua solusi begitu rumit ! Bagaimana dengan fungsi template paling sederhana dan paling umum yang menukar semua jenis ukuran dalam kondisi apa pun di sistem operasi apa pun ????

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

Ini adalah kekuatan ajaib dari C dan C ++ secara bersamaan! Cukup tukar karakter variabel asli dengan karakter.

Butir 1 : Tidak ada operator: Ingatlah bahwa saya tidak menggunakan operator penetapan sederhana "=" karena beberapa objek akan kacau ketika endianness dibalik dan pembuat salinan (atau operator penugasan) tidak akan berfungsi. Oleh karena itu, lebih baik menyalinnya dengan char.

Butir 2 : Waspadai masalah penyelarasan: Perhatikan bahwa kami menyalin ke dan dari array, yang merupakan hal yang benar untuk dilakukan karena kompiler C ++ tidak menjamin bahwa kami dapat mengakses memori yang tidak selaras (jawaban ini diperbarui dari aslinya formulir untuk ini). Misalnya, jika Anda mengalokasikan uint64_t, kompiler Anda tidak dapat menjamin bahwa Anda dapat mengakses byte ke-3 itu sebagai a uint8_t. Oleh karena itu, hal yang benar untuk dilakukan adalah menyalin ini ke array char, menukarnya, lalu menyalinnya kembali (jadi tidak reinterpret_cast). Perhatikan bahwa kompiler sebagian besar cukup pintar untuk mengubah apa yang Anda lakukan kembali ke reinterpret_castjika mereka mampu mengakses masing-masing byte terlepas dari keselarasan.

Untuk menggunakan fungsi ini :

double x = 5;
SwapEnd(x);

dan sekarang xberbeda dalam endianness.

Fisikawan Kuantum
sumber
2
Ini akan bekerja di mana saja, tetapi perakitan ocde yang dihasilkan akan sering kurang optimal: lihat pertanyaan saya stackoverflow.com/questions/36657895/…
j_kubik
Anda menggunakan new/ deleteuntuk mengalokasikan buffer untuk ini?!? sizeof(var)adalah konstanta waktu kompilasi, jadi Anda bisa melakukannya char varSwapped[sizeof(var)]. Atau Anda bisa melakukan char *p = reinterpret_cast<char*>(&var)dan bertukar di tempat.
Peter Cordes
@Peter jawaban ini cepat dan kotor dibuat untuk membuktikan suatu hal. Saya akan menerapkan saran Anda. Namun, Anda tidak harus menjadi mega SO AH dan memilih suara solusi 5-line dibandingkan dengan solusi 50-line yang diberikan di sana. Saya tidak akan mengatakan lebih banyak.
Fisikawan Kuantum
Jawaban ini membuat beberapa poin berguna tentang berhati-hati dengan konstruktor dan operator kelebihan pada data salah-endian, jadi saya akan senang untuk menghapus downvote saya setelah kode tidak mengerikan, dan merupakan sesuatu yang dapat dikompilasi oleh kompiler yang baik menjadi bswap petunjuk. Juga, saya sarankan menggunakan for(size_t i = 0 ; i < sizeof(var) ; i++)bukan static_cast<long>. (Atau sebenarnya, swap di tempat akan menggunakan naik dan turun char*sehingga hilang juga).
Peter Cordes
mis. lihat jawaban Mark Ransom menggunakan std :: swap untuk membalikkan di tempat.
Peter Cordes
3

Saya memiliki kode ini yang memungkinkan saya mengkonversi dari HOST_ENDIAN_ORDER (apa pun itu) ke LITTLE_ENDIAN_ORDER atau BIG_ENDIAN_ORDER. Saya menggunakan templat, jadi jika saya mencoba mengonversi dari HOST_ENDIAN_ORDER ke LITTLE_ENDIAN_ORDER dan keduanya sama untuk mesin yang saya kompilasi, tidak ada kode yang akan dihasilkan.

Berikut adalah kode dengan beberapa komentar:

// 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);
}
Mathieu Pagé
sumber
3

Jika integer unsigned 32-bit big-end terlihat seperti 0xAABBCCDD yang sama dengan 2864434397, maka integer 32-bit unsigned yang sama terlihat seperti 0xDDCCBBAA pada prosesor little-endian yang juga sama dengan 2864434397.

Jika sebuah big-endian 16-bit unsigned short pendek terlihat seperti 0xAABB yang sama dengan 43707, maka short notigned 16-bit yang sama itu terlihat seperti 0xBBAA pada prosesor little-endian yang juga sama dengan 43707.

Berikut adalah beberapa fungsi #define yang berguna untuk bertukar byte dari little-endian ke big-endian dan sebaliknya ->

// 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))
Adam Freeman
sumber
2

Berikut ini adalah versi umum yang saya dapatkan dari atas kepala saya, untuk menukar nilai di tempat. Saran lain akan lebih baik jika kinerja adalah masalah.

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

Penafian: Saya belum mencoba untuk mengkompilasi atau mengujinya.

Mark tebusan
sumber
2

Jika Anda mengambil pola umum untuk membalik urutan bit dalam sebuah kata, dan memilah bagian yang membalikkan bit dalam setiap byte, maka Anda ditinggalkan dengan sesuatu yang hanya membalikkan byte dalam sebuah kata. Untuk 64-bit:

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

Compiler harus membersihkan operasi bit-masking yang berlebihan (saya membiarkannya untuk menyoroti polanya), tetapi jika tidak, Anda dapat menulis ulang baris pertama dengan cara ini:

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

Itu biasanya harus disederhanakan menjadi instruksi rotasi tunggal pada sebagian besar arsitektur (mengabaikan bahwa seluruh operasi mungkin satu instruksi).

Pada prosesor RISC konstanta yang besar dan rumit dapat menyebabkan kesulitan pada kompiler. Anda dapat menghitung setiap konstanta dari yang sebelumnya dengan mudah. Seperti itu:

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

Jika suka, Anda dapat menuliskannya sebagai satu lingkaran. Itu tidak akan efisien, tetapi hanya untuk bersenang-senang:

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

Dan untuk kelengkapan, inilah versi 32-bit dari bentuk pertama yang disederhanakan:

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

Hanya berpikir saya menambahkan solusi saya sendiri di sini karena saya belum melihatnya di mana pun. Ini adalah fungsi temporer C ++ kecil dan portabel dan portabel yang hanya menggunakan operasi bit.

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;
}
Joao
sumber
2

Saya sangat terkejut tidak ada yang menyebutkan fungsi htobeXX dan betohXX. Mereka didefinisikan dalam endian.h dan sangat mirip dengan fungsi jaringan htonXX.

Nick
sumber
2

Dengan menggunakan kode di bawah ini, Anda dapat bertukar antara BigEndian dan LittleEndian dengan mudah

#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))
pz64_
sumber
1

Saya baru-baru ini menulis makro untuk melakukan ini dalam C, tetapi sama-sama valid di 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)

Ia menerima segala jenis dan membalik byte dalam argumen yang diteruskan. Contoh penggunaan:

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

Yang mencetak:

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

Di atas sempurna copy / paste-mampu, tetapi ada banyak hal yang terjadi di sini, jadi saya akan memecah cara kerjanya sepotong demi sepotong:

Hal penting pertama adalah bahwa seluruh makro terbungkus dalam sebuah do while(0)blok. Ini adalah ungkapan yang umum untuk memungkinkan penggunaan titik koma normal setelah makro.

Selanjutnya adalah penggunaan variabel bernama REVERSE_BYTES sebagai forpenghitung loop. Nama makro itu sendiri digunakan sebagai nama variabel untuk memastikan bahwa itu tidak berbenturan dengan simbol lain yang mungkin ada di ruang lingkup di mana makro digunakan. Karena nama sedang digunakan dalam ekspansi makro, itu tidak akan diperluas lagi ketika digunakan sebagai nama variabel di sini.

Dalam for loop, ada dua byte yang direferensikan dan XOR ditukar (jadi nama variabel sementara tidak diperlukan):

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

__VA_ARGS__mewakili apa pun yang diberikan kepada makro, dan digunakan untuk meningkatkan fleksibilitas dari apa yang mungkin diteruskan (meskipun tidak banyak). Alamat argumen ini kemudian diambil dan dilemparkan keunsigned char pointer untuk memungkinkan pertukaran byte-nya melalui []subscript array .

Poin aneh terakhir adalah kurangnya {}kawat gigi. Mereka tidak perlu karena semua langkah dalam setiap swap bergabung dengan operator koma , menjadikannya satu pernyataan.

Akhirnya, perlu dicatat bahwa ini bukan pendekatan yang ideal jika kecepatan adalah prioritas utama. Jika ini merupakan faktor penting, beberapa makro spesifik-jenis atau arahan khusus platform yang dirujuk dalam jawaban lain kemungkinan merupakan pilihan yang lebih baik. Pendekatan ini, bagaimanapun, adalah portabel untuk semua jenis, semua platform utama, dan kedua bahasa C dan C ++.

Ryan Hilbert
sumber
menemukan ini di suatu tempat dalam beberapa kode. bingung sih dari saya. Terima kasih untuk penjelasannya. Namun mengapa penggunaannya __VA_ARGS__?
asr9
0

Wow, saya tidak bisa mempercayai beberapa jawaban yang saya baca di sini. Sebenarnya ada instruksi dalam pertemuan yang melakukan ini lebih cepat dari yang lainnya. bswap. Anda cukup menulis fungsi seperti ini ...

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

Hal ini JAUH lebih cepat daripada intrinsik yang telah disarankan. Saya sudah membongkar dan melihat mereka. Fungsi di atas tidak memiliki prolog / epilog sehingga hampir tidak memiliki overhead sama sekali.

unsigned long _byteswap_ulong(unsigned long value);

Melakukan 16 bit sama mudahnya, dengan pengecualian bahwa Anda akan menggunakan xchg al, ah. bswap hanya berfungsi pada register 32-bit.

64-bit sedikit lebih rumit, tetapi tidak terlalu rumit. Jauh lebih baik daripada semua contoh di atas dengan loop dan template dll.

Ada beberapa peringatan di sini ... Pertama, bswap hanya tersedia pada CPU 80x486 ke atas. Adakah yang berencana menjalankannya pada 386?!? Jika demikian, Anda masih dapat mengganti bswap dengan ...

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

Perakitan inline juga hanya tersedia dalam kode x86 di Visual Studio. Fungsi telanjang tidak bisa di-line dan juga tidak tersedia di build x64. Saya misalnya, Anda harus menggunakan intrinsik kompiler.

Tukang las
sumber
1
_byteswap_ulongdan _uint64(misalnya dalam jawaban yang diterima) keduanya mengkompilasi untuk menggunakan bswapinstruksi. Saya akan terkejut tetapi tertarik untuk mengetahui apakah ASM ini jauh lebih cepat karena hanya menghilangkan prolog / epilog - apakah Anda membandingkannya?
ZachB
@stdcall Pertanyaannya tidak menanyakan solusi portabel atau bahkan menyebutkan sesuatu tentang platform. Seperti jawaban saya katakan, di atas adalah tentang cara tercepat untuk endian swap. Tentu, jika Anda menulis ini pada platform non-X86 maka ini tidak akan berhasil, tetapi seperti yang saya juga sebutkan, Anda kemudian terbatas pada kompiler intrinsik, jika kompiler Anda bahkan mendukungnya.
The Welder
@ZachB Dalam kasus khusus ini, saya pikir menghilangkan prolog dan epilog akan memberi Anda penghematan yang layak karena pada dasarnya Anda hanya menjalankan 1 instruksi. Prolog harus mendorong ke tumpukan, melakukan pengurangan, mengatur basis-pointer dan kemudian serupa di akhir. Saya belum membandingkannya, tetapi di atas memiliki 0 rantai ketergantungan yang Anda tidak akan dapatkan tanpa telanjang. Mungkin kompiler yang baik akan menyejajarnya, tetapi kemudian Anda berada di ball-park yang berbeda.
The Welder
2
Mungkin. Tetapi perhatikan bahwa dalam kasus umum menukar array angka, intrinsik kompiler yang dibahas dalam jawaban lain akan menggunakan ekstensi SSE / AVX dan memancarkan PSHUFB, yang mengungguli BSWAP. Lihat wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB
Ini bentuk buruk IMHO untuk mengirim solusi platform-spesifik, ketika OP tidak menentukan bahwa mereka hanya membutuhkan solusi untuk x86. Dan untuk meremehkan solusi lain, ketika Anda tidak dapat digunakan pada banyak OS yang sangat banyak digunakan seperti iOS dan Android (yang menggunakan CPU ARM atau MIPS.)
Jens Alfke
0

Teknik portabel untuk menerapkan aksesor endian non-inplace yang ramah bagi pengoptimal-ramah. Mereka bekerja pada setiap kompiler, setiap penyelarasan batas dan setiap pemesanan byte. Rutin yang tidak selaras ini ditambah, atau diperdebatkan, tergantung pada endian asli dan perataan. Sebagian daftar tetapi Anda mendapatkan ide. BO * adalah nilai konstan berdasarkan pemesanan byte asli.

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

Typedef ini memiliki manfaat meningkatkan kesalahan kompiler jika tidak digunakan dengan accessors, sehingga mengurangi bug accessor yang terlupakan.

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 */
BSalita
sumber
2
Untuk pertanyaan ini, tag C ++ membuat perbedaan. Ada banyak perilaku tidak terdefinisi karena C ++ dan serikat.
jww
0

Berikut cara membaca ganda yang disimpan dalam format IEEE 754 64 bit, bahkan jika komputer host Anda menggunakan sistem yang berbeda.

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

Untuk sisa rangkaian fungsi, termasuk rutinitas menulis dan integer, lihat proyek github saya

https://github.com/MalcolmMcLean/ieee754

Malcolm McLean
sumber
0

Bertukar byte dengan Anda 3-langkah-xor trik sekitar pivot dalam fungsi templat memberikan solusi O yang fleksibel dan cepat (ln2) yang tidak memerlukan pustaka, gaya di sini juga menolak jenis 1 byte:

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);
    }
}
Quinn Carver
sumber
0

Sepertinya cara aman adalah menggunakan htons pada setiap kata. Jadi, jika Anda memiliki ...

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

Di atas akan menjadi no-op jika Anda menggunakan sistem big-endian, jadi saya akan mencari apa pun yang digunakan platform Anda sebagai kondisi waktu kompilasi untuk memutuskan apakah htons adalah no-op. Lagipula itu O (n). Pada Mac, itu akan menjadi seperti ...

#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
cycollins
sumber
0

Jika Anda memiliki C ++ 17 maka tambahkan header ini

#include <algorithm>

Gunakan fungsi template ini untuk menukar byte:

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

sebut saja seperti:

swapEndian (stlContainer);
VB Thinkal
sumber
-4

Cari sedikit perubahan, karena pada dasarnya ini yang perlu Anda lakukan untuk bertukar dari sedikit -> big endian. Kemudian tergantung pada ukuran bit, Anda mengubah cara Anda menggeser bit.

Redbaron
sumber