22

需要减去两个无符号整数(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)?

4

8 回答 8

35

假设两个无符号整数:

  • 如果您知道一个应该比另一个“更大”,只需减去。只要您没有绕过一次以上,它就会起作用(显然,如果有,您将无法分辨)。
  • 如果您不知道其中一个大于另一个,请减去并将结果转换为相同宽度的有符号整数。如果两者之间的差异在签名 int 的范围内,它将起作用(如果不是,您将无法分辨)。

澄清一下:原发帖人描述的场景似乎让人困惑,但它是典型的单调递增的固定宽度计数器,例如硬件滴答计数器,或协议中的序列号。计数器(例如 8 位)0xfc、0xfd、0xfe、0xff、0x00、0x01、0x02、0x03 等,并且您知道在您拥有的两个值 x 和 y 中,x 稍后出现。如果 x==0x02 和 y==0xfe,计算 xy(作为 8 位结果)将给出 4 的正确答案,假设两个n位值的减法包含模 2 n - C99 保证减去无符号值。(注意:C 标准不保证有符号值减法的这种行为。)

于 2010-01-14T00:13:09.863 回答
22

当您从“较大”中减去“较小”时,这里有更多详细信息说明为什么它“正常工作”。

涉及到这几点...<br> 1. 在硬件中,减法使用加法:适当的操作数在相加之前被简单地取反。
2. 在二进制补码(几乎所有东西都使用)中,通过反转所有位然后加 1 来否定整数。

硬件比上面描述的听起来更有效,但这是减法的基本算法(即使值是无符号的)。

因此,让我们使用 8 位无符号整数图 2 – 250。在二进制中,我们有

  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
于 2010-05-27T14:43:10.340 回答
13

也许我不明白,但有什么问题:

unsigned r = x - y;

于 2010-01-14T00:02:19.993 回答
3

如前所述,这个问题令人困惑。您说您正在减去无符号值。如您所说,如果x始终大于y,则x - y不可能环绕或溢出。所以你只要做x - y(如果这是你需要的),就是这样。

于 2010-01-14T00:16:28.010 回答
2

这是确定循环缓冲区中可用空间量或进行滑动窗口流量控制的有效方法。使用unsigned intsheadtail- 增加它们并让它们换行!缓冲区长度必须是 2 的幂。

free = ((head - tail) & size_mask),其中size_mask2^n-1 是缓冲区或窗口大小。

于 2011-09-22T06:47:14.863 回答
1

只是将已经正确的答案放入代码中:

如果您知道 x 是较小的值,则以下计算将起作用:

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

uint8_t在这种情况下,差异必须在范围内。

代码实验帮助我更好地理解了解决方案。

于 2017-08-15T11:27:55.810 回答
1

问题应表述如下:

假设两个指针ab时钟的位置(角度)由 uint8_t 给出。整个圆周分为 uint8_t 的 256 个值。如何有效地计算两个指针之间的较小距离?

一个解决方案是:

uint8_t smaller_distance = abs( (int8_t)( a - b ) );

我怀疑没有什么比 abs() 更有效的了。

于 2017-12-20T15:40:49.163 回答
0

为了回应其他所有人的回复,如果您只是将两者相减并将结果解释为无符号,您就可以了。

除非你有明确的反例。

您的x = 0x2,的示例y= 0x14不会导致0x4,它会导致0xEE,除非您对未说明的数学有更多限制。

于 2010-01-14T00:54:54.530 回答