C Проблемы с упаковкой беззнаковых целых чисел в uint64_t

Мне предоставляется набор данных, и я должен упаковать его в значение 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-го крайнего левого бита. Я изо всех сил пытаюсь понять, что я делаю неправильно, хотя я новичок в побитовой арифметике и еще не полностью понимаю, как она взаимодействует с языком C.

Обновлено: По запросу, код для add () и subtract ()

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

Покажите декларацию weatherlog_t.

unwind 09.11.2018 14:08
typedef uint64_t weatherlog_t;
Matt Angelucci 09.11.2018 14:10
add и subtract используют unsigned int, который может быть только 32-битным.
Johnny Mopp 09.11.2018 14:13

Может ли переопределение или определение новых функций add () и sub () с возвращаемыми и типами параметров weatherlog_t решить эту проблему?

Matt Angelucci 09.11.2018 14:15

Относительно температуры: «градусы Фаренгейта, хранящиеся как 8-битовое целое число со знаком». Известно, что временные интервалы находятся в диапазоне от -129F до 134F: диапазон, который не совсем обрабатывается «8-битным целым числом со знаком». Может, по Цельсию?

chux - Reinstate Monica 09.11.2018 16:13
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
331
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

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

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

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

Каждая из ваших функций 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;
}

Я не верю, что мне разрешено изменять параметры pack_log_entry. Поможет ли их преобразование в weatherlog_t работать?

Matt Angelucci 09.11.2018 14:32

@MattAngelucci Не будет, поскольку тип аргументов будет содержать только 32 бита. Если эти функции были предоставлены вашим профессором, вам нужно вернуться к нему и объяснить, что они неисправны.

dbush 09.11.2018 14:34

Возможно, функции добавления и подгруппы предназначались только для использования с отдельными значениями, а не с 64-битным пакетом.

user3160514 09.11.2018 15:21
Ответ принят как подходящий

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

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

Это правильный ответ. Упаковка с помощью shift + или - это обычное, быстрое и чистое решение.

user3160514 09.11.2018 15:20

Маскировка предназначена не только для мусора. Вне допустимого диапазона значений. Маскировка необходима для значений со знаком.

chux - Reinstate Monica 09.11.2018 15:53

@chux Ну, если входные данные содержат отрицательные значения, вероятно, это неверно. Хотя, конечно, мы не должны использовать побитовые операторы с отрицательными значениями. Любопытно, однако, что данные в вопросе содержат температуры, которые, возможно, могут быть выражены как отрицательные с дополнением до двух. Здесь отсутствует спецификация. (Я завидую тем, кому никогда не приходится выражать температуру на улице отрицательными числами ...)

Lundin 09.11.2018 16:04

«если входные данные содержат отрицательные значения, вероятно, это неверно». -> Низкая температура <0F может быть необычной для некоторых, но не там, где мы с вами живем. Сейчас я вижу белое вещество.

chux - Reinstate Monica 09.11.2018 16:07

Проблемы:

Код не может замаскировать старшие биты значений со знаком, таких как 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;
}

Другие вопросы по теме