如何在不使用if条件的情况下计算整数绝对值。 我想我们需要使用一些按位操作。 有人可以帮忙吗?
答案 0 :(得分:73)
与现有答案相同,但有更多解释:
让我们假设一个二进制补码数(因为它是通常的情况,你没有另外说),让我们假设32位:
首先,我们执行算术右移31位。这会导致所有1
s为负数或所有0
s为正数(但请注意,实际>>
- 运算符在C或C ++中的行为是针对负数的实现定义,但通常也会执行算术移位,但我们只假设伪代码或实际硬件指令,因为它听起来像家庭作业):
mask = x >> 31;
所以我们得到的是负数的111...111
( - 1)和积极的000...000
(0)
现在我们使用x
对此进行异或,获取mask=111...111
(负)的NOT行为和mask=000...000
的无行动(正面):
x = x XOR mask;
最后减去我们的面具,这意味着+1为负数,+ 0 / no-op为正数:
x = x - mask;
因此,对于正数,我们执行XOR为0且减法为0,从而获得相同的数字。对于否定,我们得到(NOT x) + 1
,当使用二进制补码表示时,它正好是-x
。
答案 1 :(得分:26)
1)将掩码设置为整数右移31(假设整数存储为二进制补码32位值,并且右移运算符执行符号扩展)。
mask = n>>31
2)使用数字
对遮罩进行异或mask ^ n
3)从步骤2的结果中减去掩码并返回结果。
(mask^n) - mask
答案 2 :(得分:6)
假设int
是32位。
int my_abs(int x)
{
int y = (x >> 31);
return (x ^ y) - y;
}
答案 3 :(得分:2)
还可以执行上述操作:
return n*(((n>0)<<1)-1);
其中n
是绝对需要计算的数字。
答案 4 :(得分:1)
在发现这个问题之前,我写了自己的。
我的答案可能较慢,但仍然有效:
int abs_of_x = ((x*(x >> 31)) | ((~x + 1) * ((~x + 1) >> 31)));
答案 5 :(得分:1)
在C中,您可以使用联合对双打执行位操作。以下内容适用于C,可用于整数,浮点数和双精度数。
/**
* Calculates the absolute value of a double.
* @param x An 8-byte floating-point double
* @return A positive double
* @note Uses bit manipulation and does not care about NaNs
*/
double abs(double x)
{
union{
uint64_t bits;
double dub;
} b;
b.dub = x;
//Sets the sign bit to 0
b.bits &= 0x7FFFFFFFFFFFFFFF;
return b.dub;
}
请注意,这假设双精度数为8个字节。
答案 6 :(得分:0)
您使用的编程语言是什么?在C#中,您可以使用Math.Abs方法:
int value1 = -1000;
int value2 = 20;
int abs1 = Math.Abs(value1);
int abs2 = Math.Abs(value2);
答案 7 :(得分:0)
对于汇编,最有效的方法是将值初始化为0,减去整数,然后取最大值:
pxor mm1, mm1 ; set mm1 to all zeros
psubw mm1, mm0 ; make each mm1 word contain the negative of each mm0 word
pmaxswmm1, mm0 ; mm1 will contain only the positive (larger) values - the absolute value
答案 8 :(得分:0)
在 C#中,您可以在不使用任何局部变量的情况下实施 abs():
public static long abs(long d) => (d + (d >>= 63)) ^ d;
public static int abs(int d) => (d + (d >>= 63)) ^ d;
注意:关于
0x80000000 (int.MinValue)
和0x8000000000000000 (long.MinValue)
:的强>
与本页所示的所有其他按位/非分支方法一样,这给出了单个非数学结果abs(int.MinValue) == int.MinValue
(同样适用于long.MinValue
)。这些代表仅的情况,其中结果值为负数,即MSB结果的two's-complement为1
- 并且也是唯一的情况输入值保持不变的地方。我不相信这个重要的观点在本页的其他地方有所提及。
上面显示的代码取决于 xor 一侧d
使用的值{strong> xor 是d
更新期间的值C
左侧的计算。对于 C#程序员来说,这似乎是显而易见的。他们习惯于看到这样的代码,因为 .NET 正式包含了一个强大的memory model,它在这里严格保证了正确的提取顺序。我提到这一点的原因是因为在C++
或public interface Content {
public String getExternalId();
public static String externalId(Content content) {
return content == null ? null : content.getExternalId();
}
}
中可能需要更加谨慎。后者的内存模型更加宽松,这可能允许某些编译器优化发出无序提取。显然,在这种制度下,获取顺序敏感性将代表正确性危险。
答案 9 :(得分:0)
如果您不允许使用减号,则可以执行以下操作:
openssl pkcs8 -inform der -outform pem -in private.p8 -out private-p8.pem -topk8 -nocrypt
答案 10 :(得分:0)
如果在右移时不想依赖符号扩展的实现,则可以修改计算mask
的方式:
mask = ~((n >> 31) & 1) + 1
然后按照前面的答案中所示进行操作:
(n ^ mask) - mask