2010-04-20 3 views
4

Im пытается переходить от большого endian к little endian на двойном. Один путь состоит в использованииSwap bits в C++ для двоичного кода

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?

+1

Если вы делаете это на C++, я считаю, что вам следует избегать стилей C-стиля. – ereOn

+0

У меня возникла аналогичная проблема вчера, вас может заинтересовать ответ (http://stackoverflow.com/questions/2667225/endianness-conversion-and-g-warnings) –

+0

Я проверил ответ на ваш вопрос, и решение хорошо для меня, спасибо – hidayat

ответ

8

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

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

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

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

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

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

+0

Это решение определенно не работает - двойное значение, измененное с помощью endian, скорее всего, не будет действительным значением FP и вызовет неопределенное поведение в FPU. См. Ссылку @Max Lybbert. – stusmith

+0

Хорошая точка ... исправлена. – Goz