在不使用abs函数或if语句的情况下获取绝对值

时间:2012-03-19 14:52:19

标签: c bit-manipulation

我在考虑如何在不使用if语句和abs()的情况下获取整数的绝对值。起初我使用左移位(<<),尝试从范围中取出负号,然后将位移回原位,但不幸的是它对我不起作用。请让我知道它为什么不起作用以及其他替代方法。

20 个答案:

答案 0 :(得分:43)

来自Bit Twiddling Hacks

int v;           // we want to find the absolute value of v
unsigned int r;  // the result goes here 
int const mask = v >> sizeof(int) * CHAR_BIT - 1;

r = (v + mask) ^ mask;

答案 1 :(得分:24)

int abs(int v) 
{
  return v * ( (v<0) * (-1) + (v>0));
  // simpler: v * ((v>0) - (v<0))   thanks Jens
}

此代码将v的值与-11相乘得到abs(v)。因此,括号内将是-11之一。

如果v为正,则表达式(v>0)为true,值为1(v<0)为false(值为0表示false)。因此,当v为正((v>0) - (v<0)) = (1-0) = 1时。整个表达式是:v * (1) == v

如果v为否定,则表达式(v>0)为false,并且0的值为(v<0),而v为真(值为1)。因此,对于否定((v>0) - (v<0)) = (0-1) = -1v * (-1) == -v。整个表达式是:v == 0

(v<0) (v>0)v * 0 == 0评估为0时,请留下:{{1}}。

答案 2 :(得分:21)

网点 *

int abs (int n) {
    const int ret[2] = { n, -n };
    return ret [n<0];
}

注释4.7积分转换/ 4: [...] If the source type is bool, the value false is converted to zero and the value true is converted to one.


<子> * :从某种意义上说,代码中没有条件分支。在引擎盖下,三元运营商也将生产一个分支机构。但是,它也是一个有效的答案,因为三元不是if语句。这并不意味着您的编译器无法为逻辑分支的代码发出branchfree汇编代码。

答案 3 :(得分:8)

假设32位有符号整数(Java),您可以写:

public static int abs(int x)
{
    return (x + (x >> 31)) ^ (x >> 31);
}

没有乘法,没有分支。

顺便说一下,return (x ^ (x >> 31)) - (x >> 31);也会起作用,但它已获得专利。烨!

注意:此代码可能比条件语句(8位Verison)长10倍以上。这可能对硬件编程系统C等有用

答案 4 :(得分:5)

我在C中尝试这个代码,它可以工作。

int abs(int n){
   return n*((2*n+1)%2); 
}

希望这个答案会有所帮助。

答案 5 :(得分:2)

以您考虑的方式移位有符号整数是未定义的行为,因此不是一个选项。相反,你可以这样做:

int abs(int n) { return n > 0 ? n : -n; }

没有if语句,只是一个条件表达式。

答案 6 :(得分:2)

尝试以下方法:

int abs(int n) 
{
  return sqrt(n*n);
}

答案 7 :(得分:2)

这是另一种没有abs()的方法,如果没有任何逻辑/条件表达式: 假设int在这里是32位整数。这个想法很简单:(1 - 2 * sign_bit)会转换sign_bit = 1 / 0 to -1 / 1

unsigned int abs_by_pure_math( int a ) {
   return (1 - (((a >> 31) & 0x1) << 1)) * a;
}

答案 8 :(得分:2)

没见过这个。对于二进制补码表示和32位整数

( n >> 31 | 1 ) * n

答案 9 :(得分:1)

如果您的语言允许bool进行int cast(C / C ++之类):

Object

答案 10 :(得分:0)

使用三元运算符:

y = condition ? value_if_true : value_if_false;

答案 11 :(得分:0)

def absolute_value(x):
    return x * (int(x > 0) - int(x < 0))

这也适用于浮点数

答案 12 :(得分:0)

使用除法(以及更宽泛的数学)形成一个“ if”。也许效率不高,但无分支。

int abs_via_division(int v) {
  // is_neg:0 when v >= 0
  //        1 when v < 0
  int is_neg = (int) ((4LL * v) / (4LL * v + 1));
  return  v * (1 - is_neg*2);
}

int宽于long long时,对所有int都有效,除了常见的|INT_MIN|问题。

答案 13 :(得分:0)

那一个呢?

#include <climits>

long abs (int n) { // we use long to avoid issues with INT MIN value as there is no positive equivalents.
    const long ret[2] = {n, -n};
    return ret[n >> (sizeof(int) * CHAR_BIT - 1)];    // we use the most significant bit to get the right index.
}

答案 14 :(得分:0)

将符号位移出并向右移位(v << 1 >> 1)有多种原因:

  • 左移具有负值的带符号类型具有未定义的行为,因此根本不应该使用它。
  • 将值转换为unsigned会产生预期效果:如果没有填充位,(unsigned)v << 1 >> 1会删除符号位,但结果值是{{1}的绝对值仅在具有符号+幅度表示的系统上,这在当今极为罕见。在无处不在的2的补码架构中,负v的结果值为v

不幸的是,Hasturkun的解决方案具有实现定义的行为。

这是为具有带符号值的2的补码表示的系统完全定义的变体:

INT_MAX+1-v

答案 15 :(得分:0)

没有分支或乘法:

int abs(int n) {
    int mask = n >> 31;
    return (mask & -n) | (~mask & n);
}

答案 16 :(得分:0)

怎么样:

value = value > 0 ? value: ~value + 1

它基于这样的事实:负数存储为正数等价的2的补码,并且可以通过首先构建1的补码并加1来构建2的补码,所以

 5 ->   0000 0101b
-5 ->  (1111 1010b) + 1 -> 1111 1011b 

我所做的基本上是为了扭转这一点,所以

-5 ->  1111 1011b
 5 -> (0000 0100b) + 1 -> 0000 0101b

我知道这有点晚了但是我遇到了同样的问题并且落在了这里,希望这会有所帮助。

答案 17 :(得分:-2)

你必须按位组合而不是加法。

答案 18 :(得分:-3)

如果您想要一种不太昂贵的纯粹数学方法,请尝试

f(x) = (x*x)/x

或在C ++中

function abs(auto x) {return ((x*x)/x);}

答案 19 :(得分:-3)

只是有什么问题:

-1 * n

使用负减等于加原理