Поменять местами биты в С++ на двойной

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

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?


person hidayat    schedule 20.04.2010    source источник
comment
Если вы делаете это на C++, я считаю, что вам следует избегать приведения типов в стиле C.   -  person ereOn    schedule 20.04.2010
comment
Вчера у меня возникла аналогичная проблема, вас может заинтересовать ответ ( "> stackoverflow.com/questions/2667225/ )   -  person Raoul Supercopter    schedule 20.04.2010
comment
Я проверил ответ на ваш вопрос, и решение меня устраивает, спасибо   -  person hidayat    schedule 20.04.2010


Ответы (3)


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

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;
}
person GManNickG    schedule 20.04.2010

При работе с бинарным представлением чисел с плавающей запятой или удваивается.

person Max Lybbert    schedule 20.04.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;
};

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

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

person Goz    schedule 20.04.2010
comment
Это решение определенно не работает - двойник с измененным порядком байтов, скорее всего, больше не будет допустимым значением FP и вызовет неопределенное поведение в FPU. См. ссылку @Max Lybbert. - person stusmith; 20.04.2010