Поменяйте биты в с ++ для двойного - PullRequest
4 голосов
/ 20 апреля 2010

Я пытаюсь сменить порядковый номер с прямым порядком байтов на двойной. Один из способов - использовать

double val, tmp = 5.55;

((unsigned int *)&val)[0] = ntohl(((unsigned int *)&tmp)[1]);
((unsigned int *)&val)[1] = ntohl(((unsigned int *)&tmp)[0]);

Но затем я получаю предупреждение: «разыменование указателя типа-наказание нарушит правила строгого наложения имен», и я не хочу отключать это предупреждение.

Другой путь:

#define ntohll(x) ( ( (uint64_t)(ntohl( (uint32_t)((x << 32) >> 32) )) << 32) | ntohl( ((uint32_t)(x >> 32)) ) ) 

val = (double)bswap_64(unsigned long long(tmp)); //or
val = (double)ntohll(unsigned long long(tmp));

Но тогда потеряю десятичные дроби. Кто-нибудь знает хороший способ поменять биты на двойные без использования цикла for?

Ответы [ 3 ]

9 голосов
/ 20 апреля 2010

Я бы наверное попробовал что-то вроде этого:

template <typename T>
void swap_endian(T& pX)
{
    // should static assert that T is a POD

    char& raw = reinterpret_cast<char&>(pX);
    std::reverse(&raw, &raw + sizeof(T));
}

Короткий и сладкий (и относительно непроверенный). Компилятор выполнит все необходимые оптимизации. Вышесказанное четко определено для любого типа POD и не зависит от деталей реализации.

Версия копии, если вы не хотите изменять аргумент:

template <typename T>
T swap_endian_copy(T pX)
{
    swap_endian(pX);
    return pX;
}
2 голосов
/ 20 апреля 2010

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

0 голосов
/ 20 апреля 2010

Разве вы не можете просто поменять их местами?

inline unsigned long long EndianChange( double d )
{
    char ch[8];
    memcpy( ch, &d, 8 );  // Note this will be optimised out completely by pretty much every compiler.
    ch[0] ^= ch[7] ^= ch[0] ^= ch[7]; 
    ch[1] ^= ch[6] ^= ch[1] ^= ch[6];
    ch[2] ^= ch[5] ^= ch[2] ^= ch[5];
    ch[3] ^= ch[4] ^= ch[3] ^= ch[4];

    unsigned long long dRet;
    memcpy( &dRet, ch, 8 ); // Again this will get optimised out.
    return dRet;
};

Редактировать: Как указывалось, двойная переменная «can», замененная байтом, загружается в регистр, так что возвращение его из этого регистра может означать, что значение больше не является допустимым, поэтому сохраняйте его в длинном, длинном, 64-битном, чтобы избежать эта проблема.

Это все, что нужно для обмена байтами. Я не уверен, что вы пытаетесь сделать, но каждая платформа с прямым порядком байтов, которую я когда-либо использовал, использует ту же кодировку, что и little-endian, только в обратном порядке байтов. Приведенный выше код перевернет порядок байтов для вас. Практически любой компилятор просто выполнит замену байтов, а затем вернет переменную замены байтов и избавится от memcpys. Это хороший способ решения проблем с псевдонимами.

...