如何计算整数绝对值

时间:2012-08-20 16:40:28

标签: algorithm bit-manipulation

如何在不使用if条件的情况下计算整数绝对值。 我想我们需要使用一些按位操作。 有人可以帮忙吗?

11 个答案:

答案 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-complement1 - 并且也是唯一的情况输入值保持不变的地方。我不相信这个重要的观点在本页的其他地方有所提及。


上面显示的代码取决于 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