如何用环绕或溢出减去两个无符号整数

时间:2010-01-13 23:56:58

标签: c overflow int subtraction

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

8 个答案:

答案 0 :(得分:29)

假设有两个无符号整数:

  • 如果你知道一个人应该比另一个人“更大”,那就减去。它可以工作,只要你没有多次缠绕(显然,如果你有,你将无法分辨)。
  • 如果您不知道一个比另一个大,请将结果减去并转换为具有相同宽度的signed int。如果两者之间的差异在signed int的范围内(如果没有,你将无法分辨),它将起作用。

澄清一下:原始海报描述的场景似乎让人感到困惑,但典型的是单调增加固定宽度计数器,例如硬件计数器计数器或协议中的序列号。计数器(例如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用于headtail - 递增它们并让它们换行! 缓冲区长度必须是2的幂。

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

答案 5 :(得分:1)

问题应该说明如下:

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

解决方案是:

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

我怀疑没有什么比这更有效了,否则就会有比abs()更高效的东西。

答案 6 :(得分:0)

为了回应其他人的回复,如果你只是减去两个并将结果解释为无符号你就没事。

除非你有明确的反例。

x = 0x2y= 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; } 范围内。

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