Làm cách nào để chuyển đổi giữa các giá trị lớn và cuối nhỏ trong C ++?


196

Làm cách nào để chuyển đổi giữa các giá trị lớn và cuối nhỏ trong C ++?

EDIT: Để rõ ràng, tôi phải dịch dữ liệu nhị phân (giá trị dấu phẩy động chính xác kép và số nguyên 32 bit và 64 bit) từ kiến ​​trúc CPU này sang kiến ​​trúc CPU khác. Điều này không liên quan đến kết nối mạng, vì vậy ntoh () và các chức năng tương tự sẽ không hoạt động ở đây.

EDIT # 2: Câu trả lời tôi chấp nhận áp dụng trực tiếp cho trình biên dịch Tôi đang nhắm mục tiêu (đó là lý do tại sao tôi chọn nó). Tuy nhiên, có những câu trả lời rất tốt, di động hơn ở đây.


21
ntoh hton sẽ hoạt động tốt, ngay cả khi nó không liên quan gì đến kết nối mạng.
Ben Collins

2
Cách tốt nhất để đối phó với endian nói chung là đảm bảo rằng mã chạy trên cả máy chủ nhỏ và cuối lớn. Nếu điều đó làm việc, có lẽ bạn đã làm đúng. Giả sử bạn đang ở trên x86 / be thì nguy hiểm như một thực tế.
jakobengblom2

10
hton ntoh sẽ không hoạt động nếu máy là endian lớn, bởi vì người hỏi câu hỏi rõ ràng muốn thực hiện chuyển đổi.
fabspro

6
@ jakobengblom2 là người duy nhất đề cập đến điều này. Hầu như tất cả các ví dụ trên trang này đều sử dụng các khái niệm như byte "hoán đổi" thay vì thực hiện điều đó không rõ ràng về tính cuối cùng tiềm ẩn. Nếu bạn đang xử lý các định dạng tệp bên ngoài (có độ bền cuối được xác định rõ) thì việc dễ làm nhất là xử lý dữ liệu ngoài dưới dạng luồng byte và chuyển đổi luồng byte sang và từ số nguyên gốc. Tôi co rúm mỗi khi tôi thấy short swap(short x)mã, vì nó sẽ bị hỏng nếu bạn chuyển sang một nền tảng với độ bền khác nhau. Matthieu M có câu trả lời đúng duy nhất dưới đây.
Mark Lakata

3
Bạn đang nghĩ về vấn đề hoàn toàn sai. Nhiệm vụ không phải là "làm thế nào để tôi chuyển đổi giữa các giá trị cuối lớn và cuối nhỏ". Nhiệm vụ là "làm cách nào để chuyển đổi các giá trị số nguyên và dấu phẩy động theo định dạng cụ thể sang định dạng gốc của nền tảng của tôi". Nếu bạn làm đúng, định dạng gốc có thể là endian lớn, endian nhỏ, endian hỗn hợp hoặc ternary cho tất cả các mã quan tâm của bạn.
David Schwartz

Câu trả lời:


166

Nếu bạn đang sử dụng Visual C ++, hãy làm như sau: Bạn bao gồm intrin.h và gọi các chức năng sau:

Đối với số 16 bit:

unsigned short _byteswap_ushort(unsigned short value);

Đối với số 32 bit:

unsigned long _byteswap_ulong(unsigned long value);

Đối với số 64 bit:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

Số 8 bit (ký tự) không cần phải chuyển đổi.

Ngoài ra, chúng chỉ được xác định cho các giá trị không dấu mà chúng cũng hoạt động cho các số nguyên đã ký.

Đối với số float và nhân đôi, điều đó khó khăn hơn với các số nguyên đơn giản vì chúng có thể có hoặc không theo thứ tự byte của máy chủ. Bạn có thể nhận được các phao nhỏ endian trên các máy endian lớn và ngược lại.

Trình biên dịch khác có nội tại tương tự là tốt.

Trong GCC chẳng hạn, bạn có thể gọi trực tiếp một số nội dung như được ghi lại ở đây :

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

(không cần bao gồm một cái gì đó). Afaik bits.h cũng tuyên bố chức năng tương tự theo cách không phải là trung tâm gcc.

Trao đổi 16 bit, nó chỉ là một bit xoay.

Gọi nội tại thay vì tự lăn cho bạn hiệu năng và mật độ mã tốt nhất btw ..


11
Với GCC, tôi có thể sử dụng: #include <bytewap.h> int32_t bswap_32 (int32_t x) int64_t bswap_64 (int64_t x)
jmanning2k

5
__builtin_bswapXchỉ khả dụng từ GCC-4.3 trở đi
Matt Joiner

20
Nó cũng đáng chú ý là những intrinsics / luôn luôn / hoán đổi byte, họ không thích htonl, htonsvv Bạn cần phải biết từ bối cảnh tình hình của bạn khi thực sự trao đổi các byte.
Brian Vandenberg

8
@Jason vì số 8 bit giống nhau ở endian lớn và nhỏ. :-)
Nils Pipenbrinck

2
@BrianVandenberg Phải; sử dụng htonlntohlkhông phải lo lắng về bối cảnh sẽ hoạt động khi viết mã di động vì nền tảng xác định các chức năng này sẽ trao đổi nó nếu nó nhỏ / trung bình và trên cuối lớn, nó sẽ không hoạt động. Tuy nhiên, khi giải mã một loại tệp tiêu chuẩn được xác định là endian nhỏ (nói BMP), người ta vẫn phải biết bối cảnh và không thể chỉ dựa vào htonlntohl.
huyền thoại2k

86

Chỉ cần đặt:

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

cách sử dụng : swap_endian<uint32_t>(42).


3
Có một upvote. Tôi chỉ sử dụng uchars, và gán 4 đến 1, 3 đến 2, 2 đến 3 và 1 đến 4, nhưng điều này linh hoạt hơn nếu bạn có các kích cỡ khác nhau. 6 đồng hồ trên Pentium IIRC thế hệ 1. BSWAP là 1 đồng hồ, nhưng là nền tảng cụ thể.

2
@RocketRoy: Có, và nếu tốc độ trở thành một vấn đề, thì rất đơn giản để viết quá tải với các nội dung cụ thể về nền tảng và loại.
Alexandre C.

3
@MihaiTodor: Việc sử dụng các hiệp hội này để đánh máy thông qua một loạt các ký tự được tiêu chuẩn cho phép rõ ràng. Xem ví dụ. câu hỏi này .
Alexandre C.

4
@AlexandreC. Không có trong tiêu chuẩn C ++ - chỉ có trong C. Trong C ++ (mã này là) mã này là hành vi không xác định.
Rapptz

4
@Rapptz: 3.10 có vẻ rõ ràng: "Nếu một chương trình cố gắng truy cập giá trị được lưu trữ của một đối tượng thông qua một giá trị khác với một trong các loại sau đây thì hành vi không được xác định: [...] một loại char hoặc không dấu char. ". Có lẽ tôi đang thiếu một cái gì đó ở đây, nhưng với tôi khá rõ ràng rằng việc truy cập bất kỳ loại nào thông qua con trỏ char đều được cho phép rõ ràng.
Alexandre C.

75

Từ The Byte Order Fallacy của Rob Pike:

Giả sử luồng dữ liệu của bạn có số nguyên 32 bit được mã hóa nhỏ. Dưới đây là cách giải nén nó (giả sử byte không dấu):

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

Nếu đó là endian lớn, đây là cách giải nén nó:

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

TL; DR: đừng lo lắng về thứ tự gốc nền tảng của bạn, tất cả những gì được tính là thứ tự byte của luồng bạn đang đọc và tốt hơn là bạn hy vọng nó được xác định rõ.

Lưu ý: nó đã được nhận xét trong bình luận rằng không có chuyển đổi loại rõ ràng, điều quan trọng datalà một mảng của unsigned charhoặc uint8_t. Việc sử dụng signed charhoặc char(nếu được ký) sẽ dẫn đến data[x]việc được thăng cấp thành một số nguyên và data[x] << 24có khả năng chuyển 1 thành bit dấu là UB.


5
Điều này thật tuyệt, nhưng dường như nó chỉ áp dụng cho các số nguyên và các biến thể. Làm gì với phao / đôi?
Brett

1
@ v.oddou: có và không, các tệp ánh xạ bộ nhớ hoàn toàn giống với khung mạng; nếu bạn chấp nhận không đọc chúng trực tiếp, tất cả những gì quan trọng là tính cuối cùng của chúng : nếu endian nhỏ, hãy sử dụng công thức đầu tiên, nếu đó là endian lớn, hãy sử dụng thứ hai. Bất kỳ trình biên dịch nào có giá trị muối của nó sẽ tối ưu hóa các biến đổi không cần thiết nếu kết thúc cuối cùng.
Matthieu M.

2
@meowsqueak: Có, tôi sẽ mong nó hoạt động, vì chỉ thứ tự byte thay đổi, không phải thứ tự bit trong mỗi byte.
Matthieu M.

3
Trên một ghi chú liên quan lỏng lẻo, bài đăng được liên kết là một số điều khó chịu ... Chàng trai dường như rất coi trọng sự ngắn gọn, nhưng anh ta thích viết một câu nói dài về tất cả những lập trình viên xấu mà không được giác ngộ như anh ta về sự cuối cùng, thay vì thực sự giải thích tình hình và TẠI SAO giải pháp của anh ta luôn hoạt động.
Quảng cáo N

1
Nếu bạn đang sử dụng phương pháp này, hãy đảm bảo rằng bạn đã chuyển dữ liệu của mình sang (không dấu char *)
joseph

51

Nếu bạn đang làm điều này cho mục đích tương thích mạng / máy chủ, bạn nên sử dụng:

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)

Nếu bạn đang làm điều này vì một số lý do khác, một trong những giải pháp byte_swap được trình bày ở đây sẽ hoạt động tốt.


2
Tôi tin rằng thứ tự mạng là endian lớn. Các chức năng này có thể được sử dụng với ý nghĩ đó ngay cả khi bạn không sử dụng mã mạng. Tuy nhiên, không có phiên bản float ntohf hoặc htonf
Matt

2
Matt H. điều đó chỉ chính xác. Không phải tất cả các hệ thống máy tính có thứ tự byte cuối nhỏ. Nếu bạn đang làm việc, giả sử một chiếc motorolla 68k, PowerPC hoặc một kiến ​​trúc lớn khác, các chức năng này sẽ không trao đổi byte nào cả vì chúng đã theo thứ tự byte Mạng.
Frosty

2
Thật không may, htonlntohlkhông thể đi đến endian nhỏ trên nền tảng endian lớn.
Brian Vandenberg

2
@celtschk, đã hiểu; tuy nhiên, OP muốn có một cách để chuyển đổi sự kết thúc, ngay cả trong một môi trường lớn.
Brian Vandenberg

4
Để đi đến câu hỏi không thể tránh khỏi: có một số lý do cần LE cho nền tảng BE; một số định dạng tệp (bmp, fli, pcx, qtm, rtf, tga để đặt tên cho một số) sử dụng ít giá trị endian ... hoặc ít nhất, một số phiên bản của định dạng đã thực hiện cùng một lúc.
Brian Vandenberg

26

Tôi đã lấy một vài gợi ý từ bài đăng này và đặt chúng lại với nhau để tạo thành này:

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

bạn cũng phải bao gồm <cstdint> hoặc <stdint.h>, ví dụ, cho uint32_t
ady

17

Thủ tục để đi từ endian lớn đến endian nhỏ cũng giống như đi từ endian nhỏ đến endian lớn.

Dưới đây là một số mã ví dụ:

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
Hàm cuối cùng được đăng ở đây là không chính xác và nên được chỉnh sửa thành: void exchangeByteOrder (dài không dấu & ull) {ull = (ull >> 56) | ... (ull << 56); }
Eric Burnett

14
Tôi không nghĩ việc sử dụng logic - và (&&) trái ngược với bitwise - và (&) là chính xác. Theo thông số kỹ thuật C ++, cả hai toán hạng đều được chuyển đổi hoàn toàn thành bool, đây không phải là điều bạn muốn.
Trevor Robinson

16

Có một hướng dẫn lắp ráp được gọi là BSWAP sẽ thực hiện trao đổi cho bạn, cực kỳ nhanh chóng . Bạn có thể đọc về nó ở đây .

Visual Studio, hay chính xác hơn là thư viện thời gian chạy Visual C ++, có bản chất nền tảng cho việc này, được gọi là _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). Tương tự nên tồn tại cho các nền tảng khác, nhưng tôi không biết chúng sẽ được gọi là gì.


Đó là một liên kết tuyệt vời. Nó gợi lại sự quan tâm của tôi đối với trình biên dịch x86.
PP.

1
Kết quả thời gian cho BSWAP được trình bày ở đây. gmplib.org/~tege/x86-timing.pdf ... và ở đây ... agner.org/optizes/in cản_tables.pdf

12

Chúng tôi đã làm điều này với các mẫu. Bạn có thể làm một cái gì đó như thế này:

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

Nếu bạn đang làm điều này để chuyển dữ liệu giữa các nền tảng khác nhau, hãy xem các hàm ntoh và hton.


7

Giống như cách bạn làm trong C:

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

Bạn cũng có thể khai báo một vectơ ký tự không dấu, ghi nhớ giá trị đầu vào vào nó, đảo ngược các byte thành một vectơ khác và ghi nhớ các byte ra ngoài, nhưng điều đó sẽ nhận các đơn đặt hàng có cường độ dài hơn so với tw-bit, đặc biệt là với các giá trị 64 bit.


7

Trên hầu hết các hệ thống POSIX (thông qua hệ thống POSIX) không có endian.h, có thể được sử dụng để xác định mã hóa hệ thống của bạn sử dụng. Từ đó, một cái gì đó như thế này:

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

Điều này hoán đổi thứ tự (từ endian lớn đến endian nhỏ):

Nếu bạn có số 0xDEADBEEF (trên một hệ thống endian nhỏ được lưu trữ dưới dạng 0xEFBEADDE), ptr [0] sẽ là 0xEF, ptr [1] là 0xBE, v.v.

Nhưng nếu bạn muốn sử dụng nó để kết nối mạng, thì htons, htonl và htonll (và nghịch đảo của chúng ntohs, ntohl và ntohll) sẽ hữu ích cho việc chuyển đổi từ thứ tự máy chủ sang thứ tự mạng.


6
Điều đó thật buồn cười - tiêu chuẩn POSIX tại opengroup.org/onlinepub/9699919799/toc.htm không đề cập đến một tiêu đề '<endian.h> `.
Jonathan Leffler

1
Bạn có thể sử dụng htonlvà bạn bè bất kể trường hợp sử dụng có liên quan gì đến kết nối mạng hay không. Thứ tự byte mạng là kết thúc lớn, vì vậy chỉ cần coi các hàm đó là host_to_be và be_to_host. (Tuy nhiên, không giúp ích gì nếu bạn cần host_to_le.)
Peter Cordes

5

Lưu ý rằng, ít nhất là đối với Windows, htonl () chậm hơn nhiều so với đối tác nội tại của họ _byteswap_ulong (). Cái trước là một thư viện DLL gọi vào ws2_32.dll, cái sau là một hướng dẫn lắp ráp BSWAP. Do đó, nếu bạn đang viết một số mã phụ thuộc vào nền tảng, hãy ưu tiên sử dụng nội tại cho tốc độ:

#define htonl(x) _byteswap_ulong(x)

Điều này có thể đặc biệt quan trọng đối với xử lý hình ảnh .PNG trong đó tất cả các số nguyên được lưu trong Big Endian với lời giải thích "Người ta có thể sử dụng htonl () ..." {để làm chậm các chương trình Windows điển hình, nếu bạn chưa chuẩn bị}.


4

Hầu hết các nền tảng đều có tệp tiêu đề hệ thống cung cấp các hàm bytewap hiệu quả. Trên Linux, nó nằm trong <endian.h>. Bạn có thể gói nó độc đáo trong 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';
}

Đầu ra:

cafe
deadbeaf
feeddeafbeefcafe

Thay đổi: #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); }
ldav1s

Cảm ơn, quên kiểm tra betoh () và letoh ().
Maxim Egorushkin

4

tôi thích cái này, chỉ dành cho phong cách :-)

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

Tôi gặp lỗi khi char[]nói 'Lỗi: loại không đầy đủ không được phép'
Người chạy Portland

4

Nghiêm túc ... Tôi không hiểu tại sao tất cả các giải pháp phức tạp ! Làm thế nào về chức năng mẫu đơn giản nhất, tổng quát nhất hoán đổi bất kỳ loại kích thước nào trong mọi trường hợp trong bất kỳ hệ điều hành nào ????

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

Đó là sức mạnh kỳ diệu của C và C ++ cùng nhau! Đơn giản chỉ cần trao đổi các ký tự biến ban đầu theo nhân vật.

Điểm 1 : Không có toán tử: Hãy nhớ rằng tôi đã không sử dụng toán tử gán đơn giản "=" vì một số đối tượng sẽ bị rối khi lật ngược và hàm tạo sao chép (hoặc toán tử gán) sẽ không hoạt động. Vì vậy, đáng tin cậy hơn để sao chép chúng bằng char.

Điểm 2 : Lưu ý về các vấn đề căn chỉnh: Lưu ý rằng chúng tôi đang sao chép vào và từ một mảng, đó là điều nên làm vì trình biên dịch C ++ không đảm bảo rằng chúng tôi có thể truy cập bộ nhớ chưa được phân bổ (câu trả lời này đã được cập nhật từ bản gốc hình thức cho việc này). Ví dụ: nếu bạn phân bổ uint64_t, trình biên dịch của bạn không thể đảm bảo rằng bạn có thể truy cập byte thứ 3 dưới dạng a uint8_t. Do đó, điều đúng đắn cần làm là sao chép nó vào một mảng char, hoán đổi nó, sau đó sao chép lại (vì vậy không reinterpret_cast). Lưu ý rằng trình biên dịch hầu hết đủ thông minh để chuyển đổi những gì bạn đã làm trở lại reinterpret_castnếu chúng có khả năng truy cập từng byte riêng lẻ bất kể căn chỉnh.

Để sử dụng chức năng này :

double x = 5;
SwapEnd(x);

và bây giờ xlà khác nhau về tuổi thọ.


2
Điều này sẽ hoạt động ở bất cứ đâu, nhưng ocde lắp ráp được sản xuất thường sẽ không tối ưu: xem câu hỏi của tôi stackoverflow.com/questions/36657895/
Kẻ

Bạn sử dụng new/ deleteđể phân bổ bộ đệm cho việc này?!? sizeof(var)là một hằng số thời gian biên dịch, vì vậy bạn có thể làm char varSwapped[sizeof(var)]. Hoặc bạn có thể làm char *p = reinterpret_cast<char*>(&var)và trao đổi tại chỗ.
Peter Cordes

@Peter câu trả lời này là nhanh chóng và bẩn được thực hiện để chứng minh một điểm. Tôi sẽ thực hiện các đề xuất của bạn. Tuy nhiên, bạn không cần phải là một SO AH lớn và bỏ phiếu cho giải pháp 5 dòng so với các giải pháp 50 dòng được đưa ra ở đó. Tôi sẽ không nói nhiều hơn.
Nhà vật lý lượng tử

Câu trả lời này đưa ra một số điểm hữu ích về việc cẩn thận với các nhà xây dựng và các nhà khai thác quá tải đối với dữ liệu cuối cùng, vì vậy tôi rất vui khi xóa downvote của mình một khi mã không khủng khiếp và là thứ mà trình biên dịch tốt có thể biên dịch thành bswap chỉ dẫn. Ngoài ra, tôi khuyên bạn nên sử dụng for(size_t i = 0 ; i < sizeof(var) ; i++)thay vì a static_cast<long>. (Hoặc thực tế, trao đổi tại chỗ sẽ sử dụng tăng dần và giảm dần char*để dù sao đi nữa).
Peter Cordes

ví dụ: xem câu trả lời của Mark Ransom bằng cách sử dụng std :: exchange để đảo ngược tại chỗ.
Peter Cordes

3

Tôi có mã này cho phép tôi chuyển đổi từ HOST_ENDIAN_ORDER (bất kể đó là gì) sang LITTLE_ENDIAN_ORDER hoặc BIG_ENDIAN_ORDER. Tôi sử dụng một mẫu, vì vậy nếu tôi cố gắng chuyển đổi từ HOST_ENDIAN_ORDER sang LITTLE_ENDIAN_ORDER và chúng giống nhau cho máy tôi đã biên dịch, sẽ không có mã nào được tạo.

Đây là mã với một số ý kiến:

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

Nếu một số nguyên không dấu 32 bit cuối lớn trông giống như 0xAABBCCDD tương đương với 2864434394, thì số nguyên không dấu 32 bit đó trông giống như 0xDDCCBBAA trên bộ xử lý endian nhỏ cũng bằng với 2864434394.

Nếu một ký tự không dấu 16 bit lớn cuối cùng trông giống như 0xAABB tương đương với 43707, thì đoạn ngắn không dấu 16 bit đó trông giống như 0xBBAA trên bộ xử lý endian nhỏ cũng bằng 43707.

Dưới đây là một số hàm #define tiện dụng để hoán đổi byte từ little endian sang big endian và ngược lại ->

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

Đây là một phiên bản tổng quát mà tôi đã đưa ra khỏi đỉnh đầu của mình, để hoán đổi một giá trị tại chỗ. Các đề xuất khác sẽ tốt hơn nếu hiệu suất là một vấn đề.

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

Tuyên bố miễn trừ trách nhiệm: Tôi chưa thử biên dịch cái này hoặc kiểm tra nó.


2

Nếu bạn lấy mẫu chung để đảo ngược thứ tự các bit trong một từ và loại bỏ phần đảo ngược các bit trong mỗi byte, thì bạn sẽ để lại một thứ chỉ đảo ngược các byte trong một từ. Đối với 64 bit:

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

Trình biên dịch sẽ dọn sạch các hoạt động mặt nạ bit thừa (tôi để chúng vào để làm nổi bật mẫu), nhưng nếu không, bạn có thể viết lại dòng đầu tiên theo cách này:

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

Điều đó thường đơn giản hóa thành một lệnh xoay duy nhất trên hầu hết các kiến ​​trúc (bỏ qua rằng toàn bộ hoạt động có thể là một lệnh).

Trên bộ xử lý RISC, các hằng số lớn, phức tạp có thể gây khó khăn cho trình biên dịch. Tuy nhiên, bạn có thể tính toán từng hằng số từ các hằng số trước đó. Thích như vậy:

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

Nếu bạn thích, bạn có thể viết nó như một vòng lặp. Nó sẽ không hiệu quả, nhưng chỉ để cho vui:

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

Và để hoàn thiện, đây là phiên bản 32 bit được đơn giản hóa của mẫu đầu tiên:

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

2

Chỉ cần nghĩ rằng tôi đã thêm giải pháp của riêng mình ở đây vì tôi đã không thấy nó ở bất cứ đâu. Đây là một chức năng tạo khuôn mẫu C ++ nhỏ gọn và di động và di động chỉ sử dụng các hoạt động 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;
}

2

Tôi thực sự ngạc nhiên khi không ai nhắc đến các chức năng htobeXX và betohXX. Chúng được định nghĩa trong endian.h và rất giống với chức năng mạng htonXX.


2

Sử dụng các mã bên dưới, bạn có thể trao đổi giữa BigEndian và LittleEndian một cách dễ dàng

#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

Gần đây tôi đã viết một macro để làm điều này trong C, nhưng nó có giá trị như nhau trong 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)

Nó chấp nhận bất kỳ loại nào và đảo ngược các byte trong đối số được truyền. Ví dụ sử dụng:

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

Bản in nào:

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

Ở trên hoàn toàn có thể sao chép / dán, nhưng có rất nhiều thứ đang diễn ra ở đây, vì vậy tôi sẽ chia nhỏ cách thức hoạt động của từng mảnh:

Điều đáng chú ý đầu tiên là toàn bộ macro được bọc trong một do while(0)khối. Đây là một thành ngữ phổ biến để cho phép sử dụng dấu chấm phẩy bình thường sau macro.

Tiếp theo là việc sử dụng một biến có tên REVERSE_BYTESfor đếm của vòng lặp. Tên của macro được sử dụng làm tên biến để đảm bảo rằng nó không xung đột với bất kỳ ký hiệu nào khác có thể nằm trong phạm vi bất cứ nơi nào macro được sử dụng. Vì tên đang được sử dụng trong phần mở rộng của macro, nên nó sẽ không được mở rộng lại khi được sử dụng làm tên biến ở đây.

Trong forvòng lặp, có hai byte được tham chiếu và XOR hoán đổi (vì vậy không cần phải có tên biến tạm thời):

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

__VA_ARGS__đại diện cho bất cứ điều gì đã được trao cho macro và được sử dụng để tăng tính linh hoạt của những gì có thể được truyền vào (mặc dù không nhiều). Địa chỉ của đối số này sau đó được lấy và chuyển sang một unsigned charcon trỏ để cho phép hoán đổi các byte của nó thông qua mảng[] đăng ký .

Điểm đặc biệt cuối cùng là thiếu {} niềng răng. Chúng không cần thiết vì tất cả các bước trong mỗi trao đổi được nối với toán tử dấu phẩy , biến chúng thành một câu lệnh.

Cuối cùng, điều đáng chú ý là đây không phải là phương pháp lý tưởng nếu tốc độ là ưu tiên hàng đầu. Nếu đây là một yếu tố quan trọng, một số macro cụ thể theo loại hoặc chỉ thị dành riêng cho nền tảng được tham chiếu trong các câu trả lời khác có thể là một lựa chọn tốt hơn. Tuy nhiên, cách tiếp cận này có thể di động cho tất cả các loại, tất cả các nền tảng chính và cả ngôn ngữ C và C ++.


tìm thấy điều này ở đâu đó trong một số mã. làm tôi bối rối Cảm ơn đã giải thích. Tuy nhiên tại sao việc sử dụng __VA_ARGS__?
asr9

0

Ồ, tôi không thể tin được một số câu trả lời tôi đã đọc ở đây. Thực sự có một hướng dẫn trong lắp ráp mà làm điều này nhanh hơn bất cứ điều gì khác. bswap. Bạn chỉ có thể viết một hàm như thế này ...

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

Đó là NHIỀU nhanh hơn nội tại đã được đề xuất. Tôi đã tháo rời chúng và nhìn. Các chức năng trên không có phần mở đầu / kết thúc nên hầu như không có chi phí nào cả.

unsigned long _byteswap_ulong(unsigned long value);

Làm 16 bit thật dễ dàng, ngoại trừ việc bạn sử dụng xchg al, ah. bswap chỉ hoạt động trên các thanh ghi 32 bit.

64-bit khó hơn một chút, nhưng không quá. Tốt hơn nhiều so với tất cả các ví dụ trên với các vòng lặp và mẫu, v.v.

Có một số cảnh báo ở đây ... Thứ nhất bswap chỉ có sẵn trên CPU 80x486 trở lên. Có ai có kế hoạch chạy nó trên một chiếc 386 không?!? Nếu vậy, bạn vẫn có thể thay thế bswap bằng ...

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

Ngoài ra lắp ráp nội tuyến chỉ có sẵn trong mã x86 trong Visual Studio. Một chức năng trần trụi không thể được xếp hàng và cũng không có sẵn trong các bản dựng x64. Tôi ví dụ, bạn sẽ phải sử dụng nội tại của trình biên dịch.


1
_byteswap_ulong_uint64(ví dụ trong câu trả lời được chấp nhận) cả hai biên dịch để sử dụng bswaphướng dẫn. Tôi sẽ ngạc nhiên nhưng muốn biết liệu asm này có nhanh hơn nhiều không vì nó chỉ bỏ qua phần mở đầu / đoạn kết - bạn đã điểm chuẩn chưa?
ZachB

@stdcall Câu hỏi không yêu cầu một giải pháp di động hoặc thậm chí đề cập bất cứ điều gì về một nền tảng. Như câu trả lời của tôi đã nói, ở trên là về cách nhanh nhất để hoán đổi endian. Chắc chắn, nếu bạn đang viết điều này trên nền tảng không phải X86 thì điều này sẽ không hoạt động, nhưng như tôi cũng đã đề cập, sau đó bạn bị giới hạn đối với nội tại của trình biên dịch, nếu trình biên dịch của bạn thậm chí hỗ trợ chúng.
Thợ hàn

@ZachB Trong trường hợp cụ thể này, tôi nghĩ bỏ qua phần mở đầu và phần kết sẽ giúp bạn tiết kiệm đáng kể vì về cơ bản bạn chỉ thực hiện 1 hướng dẫn. Lời mở đầu sẽ phải đẩy lên ngăn xếp, thực hiện phép trừ, đặt con trỏ cơ sở và sau đó tương tự ở cuối. Tôi đã không điểm chuẩn nó, nhưng ở trên có chuỗi phụ thuộc 0 mà bạn chỉ đơn giản là sẽ không có được nếu không khỏa thân. Có thể một trình biên dịch tốt sẽ nội tuyến nó, nhưng sau đó bạn đang ở trong một công viên bóng khác.
Thợ hàn

2
Có lẽ. Nhưng lưu ý rằng trong trường hợp hoán đổi một mảng số, nội tại của trình biên dịch được thảo luận trong các câu trả lời khác sẽ sử dụng các phần mở rộng SSE / AVX và phát ra PSHUFB, vượt trội so với BSWAP. Xem wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB

IMHO là hình thức xấu để đăng một giải pháp dành riêng cho nền tảng, khi OP không chỉ định rằng họ chỉ cần một giải pháp cho x86. Và để chê bai các giải pháp khác, khi giải pháp của bạn không sử dụng được trên nhiều hệ điều hành được sử dụng rộng rãi như iOS và Android (sử dụng CPU ARM hoặc MIPS.)
Jens Alfke

0

Kỹ thuật di động để thực hiện các trình truy cập endian không gắn kết không thân thiện với trình tối ưu hóa. Chúng hoạt động trên mọi trình biên dịch, mọi căn chỉnh biên và mọi thứ tự byte. Các thói quen không được sắp xếp này được bổ sung, hoặc mooted, tùy thuộc vào endian bản địa và căn chỉnh. Danh sách một phần nhưng bạn có được ý tưởng. BO * là các giá trị không đổi dựa trên thứ tự byte gốc.

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

Các typedefs này có lợi ích là tăng các lỗi biên dịch nếu không được sử dụng với các bộ truy cập, do đó giảm thiểu các lỗi truy cập bị lãng quên.

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
Đối với câu hỏi này, thẻ C ++ tạo ra sự khác biệt. Có rất nhiều hành vi không xác định do C ++ và công đoàn.
jww

0

Dưới đây là cách đọc một bộ lưu trữ kép ở định dạng IEEE 754 64 bit, ngay cả khi máy tính chủ của bạn sử dụng một hệ thống khác.

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

Đối với phần còn lại của bộ hàm, bao gồm các thường trình ghi và các số nguyên, hãy xem dự án github của tôi

https://github.com/MalcolmMcLean/ieee754


0

Byte hoán đổi với thủ thuật 3 bước xor ye olde quanh trục trong hàm mẫu cung cấp giải pháp O (ln2) linh hoạt, nhanh chóng không yêu cầu thư viện, kiểu ở đây cũng từ chối loại 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);
    }
}

0

Có vẻ như cách an toàn sẽ là sử dụng htons trên mỗi từ. Vì vậy, nếu bạn có ...

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

Ở trên sẽ là không có nếu bạn đang ở trong một hệ thống lớn, vì vậy tôi sẽ tìm kiếm bất cứ điều gì nền tảng của bạn sử dụng như một điều kiện thời gian biên dịch để quyết định xem htons có phải là không hoạt động hay không. Đó là O (n) sau tất cả. Trên máy Mac, nó sẽ giống như ...

#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

Nếu bạn có C ++ 17 thì hãy thêm tiêu đề này

#include <algorithm>

Sử dụng hàm mẫu này để trao đổi các 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);
}

gọi nó như:

swapEndian (stlContainer);

-4

Tra cứu bit dịch chuyển, vì về cơ bản đây là tất cả những gì bạn cần làm để trao đổi từ ít -> endian lớn. Sau đó tùy thuộc vào kích thước bit, bạn thay đổi cách bạn thực hiện dịch chuyển bit.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.