(无符号)在这个abs()函数中做了什么?

问题描述 投票:0回答:5

有多种方法可以实现带或不带分支的

abs()
。我遇到了这个:

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

这是使用分支还是无分支?

为什么不能只使用

-v
,而是使用
-(unsigned)v
(unsigned)
在这里做什么?为什么它给出的结果比
-v
更正确?

c casting integer absolute-value branchless
5个回答
3
投票
 r=(v<0)?-(unsigned)v : v 

有一些方法可以在不分支的情况下计算

abs
,但这不是其中之一,因为
<
?:
运算符通常被翻译为分支指令。

假设

r
的类型为
unsigned int
,则当
unsigned
值为
v
时,将强制转换为
INT_MIN
以获得指定的行为。如果没有强制转换,当
-v
v
时,
INT_MIN
将是未定义的行为(因为
-INT_MIN
是 C 中的未定义行为)。


1
投票

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

condition ? value_if_true : value_if_false

三元运算符的意思是:

如果V<0, then the value of r becomes -V, thus effectively converting the negative number to a positive number. Else, (when v is a positive number at the beginning), set r equal to v.

unsigned 关键字通常不是必需的,只是表明您将 -V 存储为无符号数。您可以这样做,因为您确信 -V 将是一个正数。

但是,最好将 -v 声明为无符号。当 v 具体为 –2,147,483,648 时,有必要将 -v 标记为无符号,因为不存在正的 2,147,783,648 有符号 int。最大的有符号整数是 2,147,783,647。

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

如您所见,unsigned int 的范围足够大,可以容纳一个特定数字 2,147,783,648,而有符号 int 的范围则不然。


0
投票

负数通常以补码形式表示。这样做的一个影响是,可以用一定位数表示的最大可能负整数是 1,比最大可能正值更远离零。

我们以3位整数为例

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

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

IIRC,它是-4,+4 没有带符号的 3 位表示。

如果我们在 3 位整数类型内取反 100,则没有有效值。我们需要使用更广泛的整型来表示它。 unsigned 3 位类型的值范围为 0 到 7 - 4 是合法的。

如果将有符号转换为无符号,则保留相同的位值。

这个加式促销规则似乎就是这样发生的事情

我不知道类型提升规则的具体情况,这导致 有符号 111 (-1) --> 无符号 111 (7) 然后 - 无符号 111 -> 001 (1) 和 有符号 100 (-4) --> 无符号 100 (4) 然后 - 无符号 100 -> 100 (4)

但这就是它的要点。


0
投票

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

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

-1
投票

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

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

只是另一种书写方式

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

更准确地说,每个这些的编译后的代码可能是相同的。

© www.soinside.com 2019 - 2024. All rights reserved.