C 努力将无符号整数打包成 uint64_t

C Struggling with packing unsigned ints into 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;
}

由于我缺乏声誉,我无法发表评论。

当你特别需要一个整数值具有一定的符号和宽度时,你可以使用stdint.h中定义的类型。据我所知,这似乎是问题之一,给定添加和减去 return 一个无符号整数并将其包含在他们的参数中 - 它们的宽度取决于平台。 stdint.h 保证签名和宽度。由于您正在使用这两个函数并将结果添加到 uint64_t,因此您可能会在此过程中丢失字节。

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

如果您无法调整 add 和 sub 的 return 值,我建议您专门为此目的制作新的。

您的 addsub 函数各有两个类型为 unsigned int 和 return 和 unsigned int 的参数。此类型很可能小于 64 位,因此将 uint64_t 传递给这些函数之一会截断该值。

将参数类型更改为 weatherlog_t 以及函数中使用的局部变量和 return 类型。

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

我不知道你需要那些 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; 

问题:

代码无法屏蔽有符号值的高位,例如 high_temp

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

迂腐,代码无法确保扩充在范围内。屏蔽以限制影响其他字段的超出范围的值形式的另一个原因。

"it seems to stop at 32-bits" 由于 add() 限制为 32 位 add() 反正不需要。


只是 shift , mask, or.

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