需要减去两个无符号整数(x和y)。 x总是大于y。但是,x和y都可以环绕;例如,如果它们都是字节,则在0xff之后变为0x00。问题是如果x环绕,而y不环绕。现在x似乎小于y。幸运的是,x不会缠绕两次(只保证一次)。假设字节,x已经包装并且现在是0x2,而y没有并且是0xFE。 x - y的正确答案应该是0x4。
也许,
( x > y) ? (x-y) : (x+0xff-y);
但我认为还有另一种方法,涉及2s赞美?在这个嵌入式系统中,x和y是最大的unsigned int类型,因此添加0xff ...是不可能的
编写语句的最佳方法是什么(目标语言是C)?
答案 0 :(得分:29)
假设有两个无符号整数:
澄清一下:原始海报描述的场景似乎让人感到困惑,但典型的是单调增加固定宽度计数器,例如硬件计数器计数器或协议中的序列号。计数器(例如8位)0xfc,0xfd,0xfe,0xff,0x00,0x01,0x02,0x03等,你知道你拥有的两个值x和y,x后来出现。如果x == 0x02且y == 0xfe,则计算xy(作为8位结果)将给出4的正确答案,假设减去两个 n 位值包装模2 < sup> n - C99保证减去无符号值。 (注意:C标准不保证此行为可以减去签名的值。)
答案 1 :(得分:20)
当你从'较大'中减去'较小'时,这里有一个更详细的说明为什么它'正常工作'。
进入这个问题的几件事...... 1.在硬件中,减法使用加法:在添加之前简单地否定适当的操作数 2.在二进制补码(几乎所有东西都使用)中,通过反转所有位然后加1来取消整数。
硬件比上面的描述更有效地做到这一点,但这是减法的基本算法(即使值是无符号的)。
所以,让图2 - 250使用8位无符号整数。在二进制中我们有
0 0 0 0 0 0 1 0
- 1 1 1 1 1 0 1 0
我们否定要减去的操作数然后添加。回想一下,否定我们反转所有的位然后加1.反转第二个操作数的位后我们有
0 0 0 0 0 1 0 1
然后在添加1之后我们
0 0 0 0 0 1 1 0
现在我们进行添加......
0 0 0 0 0 0 1 0
+ 0 0 0 0 0 1 1 0
= 0 0 0 0 1 0 0 0 = 8, which is the result we wanted from 2 - 250
答案 2 :(得分:13)
也许我不明白,但出了什么问题:
unsigned r = x - y;
答案 3 :(得分:3)
如上所述,问题令人困惑。你说你正在减去无符号值。如果x
总是大于y
,正如您所说,那么x - y
不可能包裹或溢出。所以你只需x - y
(如果这就是你需要的那样)就是这样。
答案 4 :(得分:2)
这是确定循环缓冲区中可用空间量或滑动窗口流控制的有效方法。
将unsigned int
用于head
和tail
- 递增它们并让它们换行!
缓冲区长度必须是2的幂。
free = ((head - tail) & size_mask)
,其中size_mask
的缓冲区或窗口大小为2 ^ n-1。
答案 5 :(得分:1)
问题应该说明如下:
假设时钟的两个指针a
和b
的位置(角度)由uint8_t给出。整个循环被分成uint8_t的256个值。如何有效地计算两个指针之间的较小距离?
解决方案是:
uint8_t smaller_distance = abs( (int8_t)( a - b ) );
我怀疑没有什么比这更有效了,否则就会有比abs()更高效的东西。
答案 6 :(得分:0)
为了回应其他人的回复,如果你只是减去两个并将结果解释为无符号你就没事。
除非你有明确的反例。
x = 0x2
,y= 0x14
的示例不会产生0x4
,它会产生0xEE
,除非您对未说明的数学有更多限制。< / p>
答案 7 :(得分:0)
将已经正确的答案放入代码:
如果您知道x是较小的值,则以下计算正常:
FormLabel
如果你不知道哪一个更小:
int main()
{
uint8_t x = 0xff;
uint8_t y = x + 20;
uint8_t res = y - x;
printf("Expect 20: %d\n", res); // res is 20
return 0;
}
在这种情况下差异必须在int main()
{
uint8_t x = 0xff;
uint8_t y = x + 20;
int8_t res1 = (int8_t)x - y;
int8_t res2 = (int8_t)y - x;
printf("Expect -20 and 20: %d and %d\n", res1, res2);
return 0;
}
范围内。
代码实验帮助我更好地理解了解决方案。