2013-02-26 5 views
4

У меня есть большая проблема с ответом на этот вопрос Swap bits in c++ for a doubleКак управлять порядок байтов в два раза от сети

Тем не менее, этот вопрос более или менее то, что я ищу: я получаю двойной от сети и I хотите правильно закодировать его на моей машине.


В случае я получаю int я выполнить этот код с помощью ntohl:

int * piData = reinterpret_cast<int*>((void*)pData); 

//manage endianness of incomming network data 
unsigned long ulValue = ntohl(*piData); 
int iValue = static_cast<int>(ulValue); 

Но в случае я получаю double, я не знаю, что делать.

Ответ на этот вопрос предлагаю сделать:

template <typename T> 
void swap_endian(T& pX) 
{ 
    char& raw = reinterpret_cast<char&>(pX); 
    std::reverse(&raw, &raw + sizeof(T)); 
} 

Однако, если я процитирую this site:

The ntohl() function converts the unsigned integer netlong from network byte order to host byte order. When the two byte orders are different, this means the endian-ness of the data will be changed. When the two byte orders are the same, the data will not be changed.

напротив @ ответ GManNickG на вопрос всегда делает инверсия с std::reverse.

Я ошибаюсь, полагая, что этот ответ неверен? (в рамках сетевого управления endianess, использование которого ntohl предполагает, хотя это не было точно указано в названии вопроса OP).

В конце концов: Должен ли я разделить мой double на две части по 4 байта и применить функцию ntohl на две части? Есть ли еще канонические решения?

Существует также этот интересный вопрос в C, host to network double?, но он ограничивает значения 32 бита. И ответ говорит, что двойники должны быть преобразованы в строки из-за различий в архитектуре. Я также собираюсь работать со звуковыми образцами, должен ли я действительно рассмотреть возможность преобразования всех образцов в строки в моей базе данных? (двойники поступают из базы данных, которую я запрашиваю по сети)

+0

Нередко бывает, что данные хранятся в локальной базе данных, отличные от способа передачи данных по сетевому протоколу. Существует ли требование, чтобы сериализация данных была эквивалентной? – franji1

+0

Не можете ли вы объединить два решения, с которыми вы связаны? 'uint32_t foo = 1; if (htonl (foo)! = Foo) {/ * Решение GMan идет здесь * /} else {/ * возвращать немодифицированный аргумент * /} '. Кроме того, второй ответ правилен в том, что представления чисел с плавающей запятой являются не переносимыми, даже если это не проблема. Так вы должны преобразовать в строку или использовать для этого какую-то специальную библиотеку? Ответ зависит от того, насколько портативен этот код. – Praetorian

+0

@Praetorian, поэтому я должен думать об этом ... –

ответ

0

Если ваши парные разряды в формате IEEE 754, вы должны быть относительно хорошо. Теперь вам нужно разделить свои 64 бита на две 32-битные половинки, а затем передать их в порядке big-endian (который является порядком сети);

Как насчет:

void send_double(double d) { 
    long int i64 = *((reinterpret_cast<int *>)(&d)); /* Ugly, but works */ 
    int hiword = htonl(static_cast<int>(i64 >> 32)); 
    send(hiword); 
    int loword = htonl(static_cast<int>(i64)); 
    send(loword); 
} 

double recv_double() { 
    int hiword = ntohl(recv_int()); 
    int loword = ntohl(recv_int()); 
    long int i64 = (((static_cast<long int>) hiword) << 32) | loword; 
    return *((reinterpret_cast<double *>(&i64)); 
} 
+0

Как узнать, есть ли двойной формат в формате IEEE-754? это процессор, версия Linux или база данных, которая ограничивает кодировку? –

+0

Все вышесказанное, но в конце концов, это база данных, которая решает, какой формат сериализации она будет использовать. IEEE 754 существует уже много лет, но некоторые разработчики баз данных могут по праву выбрать осторожность и сериализацию двойников как строк, чтобы они не укусались из-за тонких несовместимостей. –

+0

Строки reinterpret_cast нарушают правило сглаживания структуры. Кроме того, 'send_double' теряет информацию, если' sizeof (int)

1

Если у вас есть параметр во время компиляции, чтобы определить порядок байт:

#if BIG_ENDIAN 
template <typename T> 
void swap_endian(T& pX) 
{ 
    // Don't need to do anything here... 
} 
#else 
template <typename T> 
void swap_endian(T& pX) 
{ 
    char& raw = reinterpret_cast<char&>(pX); 
    std::reverse(&raw, &raw + sizeof(T)); 
} 
#endif 

Конечно, другой вариант, чтобы не посылать double по сети на всех - учитывая, что не гарантируется совместимость с IEEE-754 - там есть машины, использующие другие форматы с плавающей запятой ... Использование, например, строки будет работать намного лучше ...

+0

база данных, которая будет содержать 8bytes float, представляет собой базу данных postgresql, работающую на 32-битной системе debian или ubuntu. Является ли это базой данных, версией Linux или процессором, который может сказать, совместимы ли совместимые IEEE-754? –

+0

Я имею в виду, что если вы получаете данные 'double' с другого компьютера, что сказать, что он использует тот же самый тип' double', что и ваш? Да, процессоры x86 используют двоичный формат IEEE-754. Но некоторые другие машины, с другой моделью с плавающей точкой, могут этого не делать. Если все данные просто идут между вашими локальными машинами, которыми вы управляете, вам действительно не нужно беспокоиться о контенте в первую очередь, но если данные взяты из «того, что вы не знаете, что это такое», вам также нужно беспокоиться о том, «что означают биты», а также о порядке байтов. –

1

Я не мог заставить код Джона Келлена работать на моей машине. Кроме того, это может быть более полезным для преобразования двойной в байт (8 бит, 1 символ):

template<typename T> 
string to_byte_string(const T& v) 
{ 
    char* begin_ = reinterpret_cast<char*>(v); 
    return string(begin_, begin_ + sizeof(T)); 
} 

template<typename T> 
T from_byte_string(std::string& s) 
{ 
    assert(s.size() == sizeof(T) && "Wrong Type Cast"); 
    return *(reinterpret_cast<T*>(&s[0])); 
} 

Этот код будет также работает для структур, которые используют типы POD.

Если вы действительно хотите, двойные, как два Интс

double d; 
int* data = reinterpret_cast<int*>(&d); 

int first = data[0]; 
int second = data[1]; 

Наконец, long int не всегда будет 64bit целого числа (я должен был использовать long long int, чтобы сделать 64-битную Int на моей машине).

+0

Примечание: код, который использует эти функции, должен быть осторожным, чтобы он не прерывался, если строка содержит нулевой байт. –