我得到了一个数据集合,我需要将其打包到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语言交互的。
编辑:根据请求,的加法((和减法((代码
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(独立于endian=完全可移植。
如果您怀疑每个变量包含超过指定大小的垃圾,则可能需要提前屏蔽它们:
#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的返回值,我建议专门为此制作新的。
您的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;
}
问题:
代码无法屏蔽像high_temp
这样的有符号值的高位。
奇怪的是,最后一班是6而不是7。
从教育意义上讲,代码无法确保扩充在范围内。掩码的另一个原因是限制影响其他字段的超出范围的值形式。
由于add()
被限制为32位@dbush,"它似乎停止在32位"。无论如何都不需要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;
}