簡體   English   中英

C 努力將無符號整數打包成 uint64_t

[英]C Struggling with packing unsigned ints into uint64_t

我得到了一組數據,我需要將它打包成一個 uint64_t 值,在下面的示例中,它采用“weatherlog_t”類型的形式

我不允許使用算術運算符(+、++、-、-、*、%、/、...),但是我可以使用按位運算符(&、|、^、<<、>> , ~) 和邏輯運算符 (!, =, --, !=, &&, and ||)

然而,我有預定義的 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;
}

我不知道你需要那些添加/訂閱功能; 似乎是混淆。 以特定位打包數據要簡單得多:

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

由於我缺乏聲譽,我無法發表評論。

當您特別需要一個整數值來具有特定的符號和寬度時,您可以使用stdint.h定義的類型。 據我所知,這似乎是問題之一,給定 add 和 substract 返回一個無符號整數並將其包含在它們的參數中 - 它們的寬度取決於平台。 stdint.h 保證簽名和寬度。 由於您正在使用這兩個函數並將結果添加到 uint64_t,因此您可能會在此過程中丟失字節。

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

如果您不能調整 add 和 sub 的返回值,我建議專門為此目的制作新的。

您的addsub函數都接受兩個unsigned int類型的參數並返回一個unsigned int 這種類型很可能小於 64 位,因此將uint64_t傳遞給這些函數之一會截斷該值。

將參數類型更改為weatherlog_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;
}

問題:

代碼無法屏蔽像high_temp這樣的有符號值的high_temp

奇怪的是,最后一個班次是 6 而不是 7。

迂腐,代碼無法確保擴充在范圍內。 屏蔽以限制影響其他字段的超出范圍的值形式的另一個原因。

由於add()僅限於 32 位@dbush , “它似乎停止在 32 位”。 add()無論如何都不需要。


只需shiftmask

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM