C努力将未签名的整数打包到uint64_t

时间:2018-11-09 13:06:40

标签: c math bit-manipulation packing uint64

我得到了一个数据集合,我需要将其打包成一个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()和减去()代码

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 :(得分:5)

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

答案 1 :(得分:2)

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

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

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

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

答案 2 :(得分:1)

您的addsub函数分别采用两个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;
}

答案 3 :(得分:0)

问题:

代码无法屏蔽high_temp之类的带符号值的高位。

奇怪的是,上一档是6,而不是7。

书呆子,代码无法确保扩展范围。屏蔽以限制超出范围值的形式影响其他字段的另一个原因。

“由于add()限于32位@dbush,“它似乎停止在32位”。无论如何都不需要add()


只需 shift mask

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