C Борьба с упаковкой беззнаковых целых в uint64_t - PullRequest
0 голосов
/ 09 ноября 2018

Мне дан набор данных, и мне необходимо упаковать его в значение uint64_t, которое в следующем примере принимает форму типа "weatherlog_t"

Мне запрещено использовать арифметические операторы (+, ++, -, -, *,%, /, ...), однако мне разрешено использовать побитовые операторы (&, |, ^, << , >>, ~) и логические операторы (!, =, -,! =, && и ||)

Однако у меня есть предопределенные функции add () и sub (), которые обрабатывают побитовое сложение и вычитание, и они используются в следующем примере. Они были проверены, и я уверен, что они работают в той степени, в которой это необходимо.

Согласно инструкции, 64-битное значение должно быть расположено следующим образом:

    /* - year :: 6 bits -- stored as the number of years since the year 2000.
    - month :: 4 bits
    - day :: 5 bits
    - zip_code :: 16 bits
    - high_temp :: in degrees Fahrenheit, stored as an 8-bit signed integer
    - low_temp :: in degrees Fahrenheit, stored as 8-bit signed integer
    - precipitation :: in mm. stored as a 10-bit unsigned integer.
    - average_wind_speed :: 7 bits. unsigned int km/hr.

    All of these are packed into a 64 bit unsigned integer in the above order.

    We'd store:
- year :: 2015, which is 15 years from 2000, so 001111
- month :: September, which is the 9th month, so 1001.
- day :: 16, which is 1 0000
- zip_code :: 19122 which is 0100 1010 1011 0010
- high_temp :: 85F, so 0101 0101
- low_temp :: 65F, so 0100 0001
- precipitation :: 35 mm so 00 0010 0011
- average wind speed :: 5 km/h, so 000 0101

And all would be packed into a single 64-bit unsigned integer:

00 1111 1001 10000 0100 1010 1011 0010 0101 0101 0100 0001 00 0010 0011 000 0101

OR

0011 1110 0110 0000 1001 0101 0110 0100 1010 1010 1000 0010 0001 0001 1000 0101 */

Пока что у меня есть:

weatherlog_t pack_log_entry(unsigned int year, unsigned int month, unsigned int day,
                        unsigned int zip, int high_temp, int low_temp,
                        unsigned int precip, unsigned int avg_wind_speed) {


weatherlog_t ret = 0;

unsigned int newYear = sub(year, 2000);

ret = (ret << 6);
ret = add(ret, newYear);

ret = (ret << 4);
ret = add(ret, month);

ret = (ret << 5);
ret = add(ret, day);

ret = (ret << 16);
ret = add(ret, zip);

ret = (ret << 8);
ret = add(ret, high_temp);

ret = (ret << 8);
ret = add(ret, low_temp);

ret = (ret << 10);
ret = add(ret, precip);

ret = (ret << 6);
ret = add(ret, avg_wind_speed);


return ret;
}

Однако, когда я захожу и проверяю это, проверяя двоичное значение ret, кажется, что он останавливается на 32-битах, а смещение влево после этой точки приводит к потере любых битов, оставшихся от 32-го левого бита. Я изо всех сил пытаюсь понять, что я делаю неправильно, хотя я новичок в побитовой арифметике и пока не до конца понимаю, как он взаимодействует с языком Си.

РЕДАКТИРОВАТЬ: По запросу, код для add () и вычитания ()

unsigned int add(unsigned int i, unsigned int j) {
/* can be done in a total of 7 lines, including one to declare an unsigned int, */
/* two for a while loop, and one for the return
 You're not required to do it in 7 lines though . */
while(j != 0){
    unsigned int carry = i & j;

    i = i ^ j;

    j = carry << 1;
}

return i;
}


unsigned int sub(unsigned int i, unsigned int j) {
/* Similar 7 lines, although there is a shorter way */
while (j != 0){
    unsigned int borrow = (~i) & j;

    i = i ^ j;

    j = borrow << 1;
}

return i;
}

Ответы [ 4 ]

0 голосов
/ 09 ноября 2018

Проблемы:

Код не в состоянии маскировать старшие биты со знаковыми значениями, например high_temp.

Последний сдвиг, как ни странно, 6, а не 7.

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

"кажется, что он останавливается на 32 битах" из-за add(), ограниченного 32 битами @ dbush .add() не требуется в любом случае.


Просто смещение , маска , или .

#define N_YEAR 6
#define N_MONTH 4
#define N_DAY 5
#define N_ZIP 16
#define N_HTEMP 8
#define N_LTEMP 8
#define N_PREC 10
#define N_AWS 7
#define MSK(bw) ((1u << (bw)) - 1)

weatherlog_t pack_log_entry(unsigned int year, unsigned int month,
    unsigned int day, unsigned int zip, int high_temp, int low_temp,
    unsigned int precip, unsigned int avg_wind_speed) {

  weatherlog_t ret = 0;
  ret = (ret << N_YEAR)  | (sub(year, 2000) & MSK(N_YEAR));
  ret = (ret << N_MONTH) | (month           & MSK(N_MONTH));
  ret = (ret << N_DAY)   | (day             & MSK(N_DAY));
  //... others
  ret = (ret << N_PREC)  | (precip          & MSK(N_PREC)) ;
  ret = (ret << N_AWS)   | (avg_wind_speed  & MSK(N_AWS));
  return ret;
}
0 голосов
/ 09 ноября 2018

Каждая из функций add и sub принимает два аргумента типа unsigned int и возвращает unsigned int. Этот тип, скорее всего, меньше 64 бит, поэтому передача uint64_t в одну из этих функций усекает значение.

Измените типы параметров на weatherlog_t, а также локальные значения, используемые в функциях и возвращаемые типы.

weatherlog_t add(weatherlog_t i, weatherlog_t j) {
    /* can be done in a total of 7 lines, including one to declare an unsigned int, */
    /* two for a while loop, and one for the return
     You're not required to do it in 7 lines though . */
    while(j != 0){
        weatherlog_t carry = i & j;

        i = i ^ j;

        j = carry << 1;
    }

    return i;
}

weatherlog_t sub(weatherlog_t i, weatherlog_t j) {
    /* Similar 7 lines, although there is a shorter way */
    while (j != 0){
        weatherlog_t borrow = (~i) & j;

        i = i ^ j;

        j = borrow << 1;
    }

    return i;
}
0 голосов
/ 09 ноября 2018

Понятия не имею, для чего вам нужны эти функции add / sub; похоже на запутывание. Упаковать данные в определенные биты гораздо проще:

#define YEAR_POS  58
#define MONTH_POS 48

ret = (uint64_t)year  << YEAR_POS  |
      (uint64_t)month << MONTH_POS |
       ...

Это имеет следующие преимущества: 1) быстрый и 2) независимый от порядка байтов = полностью переносимый.

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

#define YEAR_SIZE 6
year &= (1u << YEAR_SIZE)-1; 
0 голосов
/ 09 ноября 2018

Я не могу комментировать из-за отсутствия репутации.

Если вам необходимо целочисленное значение для определенной подписи и ширины, вы можете использовать типы, определенные в stdint.h. Из того, что я могу сказать, это, кажется, является одной из проблем, учитывая, что add и substract возвращают целое число без знака и имеют его в своих аргументах - насколько они широки, зависит от платформы. stdint.h гарантирует подпись и ширину. Поскольку вы используете эти две функции и добавляете результат в uint64_t, вы можете потерять байты в процессе.

https://www.gnu.org/software/libc/manual/html_node/Integers.html

Если вы не можете настроить возвращаемое значение add и sub, я бы предложил создать новые специально для этой цели.

...