abs没有分支,为什么这段代码有效

时间:2014-12-30 23:27:04

标签: c integer branch bit

有很多版本的实现abs()没有分支。我遇到过这个:

r=(v<0)?-(unsigned)v : v

首先,它实际上是在做某种“分支”可能是在编译器或机器代码的级别(所以不是真正的语法分支)(好吧我确认它正在进行分支)。第二,为什么不能只使用 v ,而不是 - (unsigned)v ? (签名)做了什么?以及为什么它以 -v

提供正确的结果

5 个答案:

答案 0 :(得分:3)

 r=(v<0)?-(unsigned)v : v 

有一些方法可以在没有分支的情况下计算abs,但这一方法不是其中之一,因为<?:运算符通常会转换为分支指令。

假设r属于unsigned int类型,当unsigned值为v时,转化为INT_MIN就会获得指定行为。如果没有强制转换,-v vINT_MIN时,-INT_MIN将是未定义的行为(因为{{1}}是C中未定义的行为)。

答案 1 :(得分:1)

C三元运算符的工作原理如下:

condition ? value_if_true : value_if_false

三元运营商因此说:

如果V <0,则r的值变为-V,从而有效地将负数转换为正数。否则,(当v是开头的正数时),将r设为等于v。

unsigned关键字通常不是必需的,只是表示您将-V存储为无符号数。你可以这样做,因为你肯定知道-V将是一个正数。

但是,最好声明-v unsigned。当v特别是 -2,147,483,648 时,将-v标记为无符号变为必要,因为没有正数2,147,783,648符号int。最大的signed int是2,147,783,647。

signed int的值范围是 -2,147,483,648 2,147,483,647 ,而unsigned int的值范围是 0到4,294,967,295

如您所见,unsigned int的范围足以容纳一个特定的数字2,147,783,648,而signed int的范围不是。

答案 2 :(得分:0)

负数通常以二进制补码形式表示。这样做的一个结果是,可以用一定数量的比特表示的最大可能的负整数比零最大可能的正值更远。

让我们使用3位整数作为示例

000是0
001是1 111是-1
010是2 110是-2 011是3 101是-3

什么是100?这是两个系列中的下一个值。

IIRC,它是-4,并且没有签名的3位+4表示。

如果我们在保持3位整数类型的情况下否定100,则没有有效值。我们需要使用更宽的整数类型来表示它。 无符号 3位类型的值范围为0到7 - 4是合法的。

如果将signed转换为unsigned,则保持相同的位值。

这种加型促销规则似乎正在发生的事情

我不知道类型促销规则的具体细节,原因 签名111(-1) - &gt;无符号111(7)然后 - 无符号111 - &gt; 001(1) 和 签名100(-4) - &gt;无符号100(4)然后 - 无符号100 - &gt; 100(4)

但这必须是它的主旨。

答案 3 :(得分:0)

对于二进制补码有符号整数 a ,假设sizeof(int)* 8是有符号整数中的位数,并且有符号整数上的右移复制了符号位,所以a&gt;&gt; (sizeof(int)* 8-1))是0或-1,您可以使用:

int a;
/* ... */
    a = (a + (a >> (sizeof(int)*8-1))) ^ (a >> (sizeof(int)*8-1));

答案 4 :(得分:-1)

? :C中的构造实际上是一个分支。

r = (v<0) ? - (unsigned)v : v;

只是另一种写作方式

if (v < 0) {
  r = - (unsigned)v;
} else {
  r = v;
}

更准确地说,每个结果的编译代码可以合理地相同。