Как хранить независимый двоичный код

Несмотря на то, что компьютеры с прямым порядком байтов не очень широко используются, я хочу сохранить двойной тип данных в независимом формате.

Для int это действительно просто, поскольку сдвиги битов делают это очень удобным.

int number;
int size=sizeof(number);
char bytes[size];

for (int i=0; i<size; ++i)
bytes[size-1-i] = (number >> 8*i) & 0xFF;

Этот фрагмент кода хранит число в формате с прямым порядком байтов, несмотря на машину, на которой он запускается. Какой самый элегантный способ сделать это за двойной?

3

Решение

Лучший способ для переносимости и учета формата — это сериализация / десериализация мантиссы и экспоненты по отдельности. Для этого вы можете использовать функции frexp () / ldexp ().

Например, для сериализации:

int exp;
unsigned long long mant;

mant = (unsigned long long)(ULLONG_MAX * frexp(number, &exp));

// then serialize exp and mant.

А потом десериализовать

// deserialize to exp and mant.

double result = ldexp ((double)mant / ULLONG_MAX, exp);
7

Другие решения

Изящная вещь, которую нужно сделать, — ограничить проблему порядка байтов как можно меньшим. Эта узкая область является границей ввода / вывода между вашей программой и внешним миром. Например, функции, которые отправляют двоичные данные в / получают двоичные данные из некоторого другого приложения, должны знать о проблеме порядка байтов, как и функции, которые записывают двоичные данные в / считывают двоичные данные из некоторого файла данных. Сделайте эти интерфейсы осведомленными о проблеме представления.

Сделайте все остальное блаженно неосведомленным о проблеме. Используйте местное представительство везде. Представить число с плавающей запятой двойной точности как double а не массив из 8 байтов, представляет 32-битное целое число как int или же int32_t а не массив из 4 байтов, и так далее. Решение проблемы с порядком байтов по всему коду сделает ваш код раздутым, подверженным ошибкам и уродливым.

5

Такой же. Любой числовой объект, в том числе double, в конечном итоге занимает несколько байтов, которые интерпретируются в определенном порядке в соответствии с порядком байтов. Таким образом, если вы измените порядок байтов, вы получите точно такое же значение в обратном порядке байтов.

1

 char *src_data;
char *dst_data;

for (i=0;i<N*sizeof(double);i++) *dst_data++=src_data[i ^ mask];
// where mask = 7, if native == low endian
// mask = 0, if native = big_endian

Элегантность заключается в mask который обрабатывает также короткие и целочисленные типы: это sizeof (elem) -1, если порядковый номер цели и источника различается.

1

Не очень переносимый и нарушающий стандарты, но как то так:

std::array<unsigned char, 8> serialize_double( double const* d )
{
std::array<unsigned char, 8> retval;
char const* begin = reinterpret_cast<char const*>(d);
char const* end = begin + sizeof(double);
union
{
uint8  i8s[8];
uint16 i16s[4];
uint32 i32s[2];
uint64 i64s;
} u;
u.i64s = 0x0001020304050607ull; // one byte order
//  u.i64s = 0x0706050403020100ull; // the other byte order

for (size_t index = 0; index < 8; ++index)
{
retval[ u.i8s[index] ] = begin[index];
}
return retval;
}

может обрабатывать платформу с 8-битными символами, 8-байтовыми двойными числами и любым порядком байтов сумасшедшей задницы (например, с прямым порядком байтов в словах, но с прямым порядком байтов между словами для 64-битных значений, например).

Теперь, это не покрывает порядок байтов double, отличающийся от порядкового номера 64-битных целых.

Более простым подходом может быть приведение вашего двойного к 64-битному значению без знака, а затем вывод его, как и любой другой тип int.

0
void reverse_endian(double number, char (&bytes)[sizeof(double)])
{
const int size=sizeof(number);
memcpy(bytes, &number, size);
for (int i=0; i<size/2; ++i)
std::swap(bytes[i], bytes[size-i-1]);
}
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector