вытаскивание 16-ти бит
От: ra88  
Дата: 22.04.09 11:34
Оценка:
Приветствую!

У меня такая задача. Дано целое беззнаковое число 32-битное или 64-битное.
Надо поотдельности вытащить из него младшие 16 бит, следующие 16 бит
и 16 бит после них.

Вопрос, как лучше всего сделать? У меня два варианта:
1.
typedef unsigned long long uint64;
typedef unsigned int       uint32;
typedef unsigned short     uint16;

#define HH16(x) ((uint16)(((uint64)x & 0xFFFF00000000LU) >> 32))
#define HI16(x) ((uint16)(((uint64)x & 0xFFFF0000LU) >> 16))
#define LO16(x) ((uint16)((uint64)x & 0xFFFFLU))

// затем:
....
uint64 x = 328237324242L;
uint32 y = 324982372
printf("word0 = %u, word1= %u, word2 = %u\n", HH16(x), HI16(x), LO16(x));
printf("word0 = %u, word1= %u, word2 = %u\n", HH16(y), HI16(y), LO16(y));

либо
2.
typedef struct {
    uint16 word0;
    uint16 word1;
    uint16 word2;
} three16_t;

// затем
....
uint64 x = 328237324242L;
uint32 y = 324982372
three16_t tmp;

tmp = *((three16_t*)&x);
printf("word0 = %u, word1= %u, word2 = %u\n", tmp.word2, tmp.word1, tmp.word0);

tmp = *((three16_t*)&y);
printf("word0 = %u, word1= %u, word2 = %u\n", tmp.word2, tmp.word1, tmp.word0);


Кто из них быстрее? Не будет ли проблем у второго метода на big endian машине c другим компилятором?

язык C (gcc 4.3.2), платформа linux
while true;
Re: вытаскивание 16-ти бит
От: spring  
Дата: 22.04.09 13:13
Оценка: 1 (1)
Здравствуйте, ra88, Вы писали:

R>Приветствую!


R>У меня такая задача. Дано целое беззнаковое число 32-битное или 64-битное.

R>Надо поотдельности вытащить из него младшие 16 бит, следующие 16 бит
R>и 16 бит после них.

R>Вопрос, как лучше всего сделать? У меня два варианта:

R>1.
R>
R>typedef unsigned long long uint64;
R>typedef unsigned int       uint32;
R>typedef unsigned short     uint16;

R>#define HH16(x) ((uint16)(((uint64)x & 0xFFFF00000000LU) >> 32))
R>#define HI16(x) ((uint16)(((uint64)x & 0xFFFF0000LU) >> 16))
R>#define LO16(x) ((uint16)((uint64)x & 0xFFFFLU))

R>// затем:
R>....
R>uint64 x = 328237324242L;
R>uint32 y = 324982372
R>printf("word0 = %u, word1= %u, word2 = %u\n", HH16(x), HI16(x), LO16(x));
R>printf("word0 = %u, word1= %u, word2 = %u\n", HH16(y), HI16(y), LO16(y));

быстрее

А ещё можно сделать так:

R>#define HH16(x) ((uint16)((((uint64)x << 16) >> 48))
R>#define HI16(x) ((uint16)((((uint64)x << 32) >> 48))
R>#define LO16(x) ((uint16)((((uint64)x << 48) >> 48))

скорее всего будет ещё быстрее :)

R>

R>либо
R>2.
Будут проблеммы на big endian, и скорее всего будет медленней, но не факт — зависит от компилятора.
Re: вытаскивание 16-ти бит
От: Alexander G Украина  
Дата: 22.04.09 13:14
Оценка: 2 (1)
Здравствуйте, ra88,

почему второй вариант не годится
Автор: McSeem2
Дата: 23.08.07
Русский военный корабль идёт ко дну!
Re: вытаскивание 16-ти бит
От: Андрей Тарасевич Беларусь  
Дата: 22.04.09 13:47
Оценка: 2 (1)
Здравствуйте, ra88, Вы писали:

R>Кто из них быстрее? Не будет ли проблем у второго метода на big endian машине c другим компилятором?


У второго метода много проблем.

Во-первых — действительно будут проблемы на big endian.

Во-вторых, грубая "реинтерпретация" памяти занатого объектом одного типа, как объект другого типа приводит в С/С++ к неопределенному поведению (с редкими исключениями). Это выливается во вполне реальные проблемы в том же компиляторе gcc, который уже в режиме оптимизации -O2 подразумевает 'strict aliasing'

http://www.cellperformance.com/mike_acton/2006/06/understanding_strict_aliasing.html

Сделать "второй вариант" можно, но не так, как в исходном примере. Чтобы это работало в gcc, реинтерпретация должна либо делаться через тип 'char', либо через union, содержащий поле исходного типа и набор полей целевого типа.
Best regards,
Андрей Тарасевич
Re: вытаскивание 16-ти бит
От: AleksandrN Россия  
Дата: 23.04.09 08:32
Оценка:
Здравствуйте, ra88, Вы писали:

union a {
    uint64 ui64;
    uint32 ui32[2];
    uint16 ui16[4];
};
Re: вытаскивание 16-ти бит
От: valker  
Дата: 23.04.09 08:34
Оценка:
Здравствуйте, ra88, Вы писали:

R>Приветствую!


R>У меня такая задача. Дано целое беззнаковое число 32-битное или 64-битное.

R>Надо поотдельности вытащить из него младшие 16 бит, следующие 16 бит
R>и 16 бит после них.

Не понял, что значит "следующие 16 бит" и "16 бит после них"?

А вообще конечно union с массивами простых типов.. и индексы в зависимости от little/big endian.. imho.
Re[2]: вытаскивание 16-ти бит
От: nikholas Россия  
Дата: 23.04.09 09:13
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Сделать "второй вариант" можно, но не так, как в исходном примере. Чтобы это работало в gcc, реинтерпретация должна либо делаться через тип 'char', либо через union, содержащий поле исходного типа и набор полей целевого типа.


Насколько я понимаю, использование union для этих целей также приводит к UB. Ведь мы читаем/пишем при помощи не указателя на union, а указателя на его member, и доступ происходит по типу мембера => отсюда все те же проблемы
Re[2]: вытаскивание 16-ти бит
От: Alexander G Украина  
Дата: 23.04.09 09:16
Оценка:
Здравствуйте, valker, Вы писали:

V>А вообще конечно union с массивами простых типов.. и индексы в зависимости от little/big endian.. imho.


Ну и зачем union каст, который:
1. имеет зависимые от endian индексы.
2. несмотря на поддержку компиляторами, таки не соответствует стандарту.
3. вряд ли приведёт к генерации лучшего кода, чем получается через сдвиги.
Русский военный корабль идёт ко дну!
Re: вытаскивание 16-ти бит
От: Кодт Россия  
Дата: 23.04.09 10:24
Оценка: +1
Здравствуйте, ra88, Вы писали:

R>
R>typedef unsigned long long uint64;
R>typedef unsigned int       uint32;
R>typedef unsigned short     uint16;

R>#define HH16(x) ((uint16)(((uint64)x & 0xFFFF00000000LU) >> 32))
R>#define HI16(x) ((uint16)(((uint64)x & 0xFFFF0000LU) >> 16))
R>#define LO16(x) ((uint16)((uint64)x & 0xFFFFLU))
R>


А зачем с такими масками?
#define Word0(x) ((uint16)(x))
#define Word1(x) Word0((x) >> 16)
#define Word2(x) Word1((x) >> 16)
#define Word3(x) Word2((x) >> 16)

Единственно, нужно посмотреть — не косячит ли компилятор на больших сдвигах маленьких чисел. Т.е.
uint16 x;
uint16 w0 = Word0(x); // тут всё в порядке
uint16 w1 = Word1(x), // x>>16 =!!!= x>>(16%16) == x>>0 == x, а должно было быть 0

uint32 y;
uint16 w2 = Word2(y); // y>>16>>16 == y>>32 =!!!= y>>(32%32) == y>>0 == y

В этом случае нужно пропатчить — расширив тип аргумента до ожидаемого и нормально работающего
#define Word1(x) Word0((uint32)(x) >> 16)
#define Word2(x) Word0((uint64)(x) >> 32)
#define Word3(x) Word0((uint64)(x) >> 48)


А ещё — интересно, какое поведение ожидается от знаковых чисел? Т.е. -1 трактуется как "абсолютный", 0xFFFF......F, или как только 32-битный 0x00000000FFFFFFFF (ну или какая там разрядность у int)?
И под это дело подкручивать формулы.

Либо можно договориться, что аргументы этих макросов — строго беззнаковые, а кто подсунул левятину — ССЗБ.
... << RSDN@Home 1.2.0 alpha 4 rev. 1181>>
Перекуём баги на фичи!
Re: вытаскивание 16-ти бит
От: Андрей Тарасевич Беларусь  
Дата: 23.04.09 18:24
Оценка: +1
Здравствуйте, ra88, Вы писали:

R>1.

R>
R>typedef unsigned long long uint64;
R>typedef unsigned int       uint32;
R>typedef unsigned short     uint16;

R>#define HH16(x) ((uint16)(((uint64)x & 0xFFFF00000000LU) >> 32))
R>#define HI16(x) ((uint16)(((uint64)x & 0xFFFF0000LU) >> 16))
R>#define LO16(x) ((uint16)((uint64)x & 0xFFFFLU))

И если идти по этому пути, то, если тип 'uint16' именно содержит ровно 16 требуемых битов, то вышеприведенные операции можно записать проще. В частности, 'LO16' можно записать как просто 

[ccode]
#define LO16(x) ((uint16)(x))


и не надо никаких явных битовых операций. А компилятор уже сам разберется, как ему лучше выполнить это приведение.

Соответственно

#define HI16(x) LO16((uint64)(x) >> 16)
#define HH16(x) LO16((uint64)(x) >> 32)
Best regards,
Андрей Тарасевич
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.