我在考虑如何在不使用if
语句和abs()
的情况下获取整数的绝对值。起初我使用左移位(<<
),尝试从范围中取出负号,然后将位移回原位,但不幸的是它对我不起作用。请让我知道它为什么不起作用以及其他替代方法。
答案 0 :(得分:43)
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
的值与-1
或1
相乘得到abs(v)。因此,括号内将是-1
或1
之一。
如果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) = -1
,v * (-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
使用负减等于加原理