如何使用位操作在java中获取数字的绝对值
Posted
技术标签:
【中文标题】如何使用位操作在java中获取数字的绝对值【英文标题】:how to get absolute value of a number in java using bit manipulation 【发布时间】:2014-02-21 02:03:13 【问题描述】:我想在java中实现一个函数来获取一个数字的绝对值:如果是正数则不做任何事情,如果是负数则转换为正数。
我只想使用位操作而不是数字比较器来做到这一点。
请帮忙
【问题讨论】:
【参考方案1】:一个否定:
-n
与二进制补码相同:
~n + 1
问题在于,如果值
n >>> 31
补码与全 1 的 XOR 相同,类似于(对于 4 位整数):
~1010 == 1010 ^ 1111
我们可以通过算术右移得到一个掩码:
n >> 31
绝对值说:
如果n 否则,什么也不做所以把它放在一起我们可以做以下事情:
static int abs(int n)
return (n ^ (n >> 31)) + (n >>> 31);
计算:
如果 n 否则,将其与全 0 进行异或并添加 0我不确定是否有简单的方法可以在不添加的情况下做到这一点。加法涉及任意数量的进位,即使是简单的增量。
例如 2 + 1 没有进位:
10 + 1 == 11
但是 47 + 1 有 4 个进位:
101111 + 1 == 110000
使用按位/位移位进行加法和进位基本上只是一个循环展开且毫无意义。
(编辑!)
傻了,这里有一个增量和进位:
static int abs(int n)
int s = n >>> 31;
n ^= n >> 31;
int c;
do
c = (n & s) << 1;
n ^= s;
while((s = c) != 0);
return n;
它的工作方式是翻转第一位,然后继续翻转直到找到 0。所以工作就是展开循环。循环体可以用一个有点可笑的复合单线来表示。
static int abs(int n)
int s = n >>> 31;
n ^= n >> 31;
int c = (n & s) << 1;
c = ((n ^= s) & (s = c)) << 1; // repeat this line 30 more times
n ^= s;
return n;
所以有一个只使用按位和位移的绝对值。
这些并不比 Math.abs 快。 Math.abs 只返回n < 0 ? -n : n
,这是微不足道的。相比之下,实际上循环展开完全糟糕。我猜只是好奇。这是我的基准:
(bit hacks abs 是非专利的shown here,这与我的想法基本相同,只是有点难以理解。)
【讨论】:
不错的解决方案。我的仍然需要一个比较。 谢谢。我想没有面具是没有办法的 为了傻笑,我添加了一个没有添加的版本。 请注意Math.abs(Integer.MIN_VALUE)
的结果是-2147483648
。【参考方案2】:
您可以通过取逻辑否定来将二进制恭维数变为正数或负数
i = ~i; // i equals not i
您可以使用Math.max()
函数始终获得肯定
public static int abs(int i)
return Math.max(i,~i);
【讨论】:
【参考方案3】:这取决于您使用的号码类型。对于 int,使用
int sign = i >> 31;
这得到符号位,0 表示正数,1 表示负数。对于其他基元类型,将 31 替换为基元使用的位数减 1。
然后您可以在 if 语句中使用该符号。
if (sign == 1)
i = ~i + 1;
【讨论】:
我想出了第一部分,只是我想知道i = -i
的按位替代方案@
真的没有。你可以做 i = ~i + 1,但这仍然不是完全按位。【参考方案4】:
我想你会发现这首小曲正是你要找的:
int abs(int v)
int mask = v >> Integer.SIZE - 1;
return v + mask ^ mask;
它基于Bit Twiddling Hacks 绝对值方程,不使用比较操作。如果您不允许使用加法,那么(v ^ mask) - mask
是一个替代方案。这个函数的价值是相当值得怀疑的;因为它几乎和 Math.abs 的实现一样清晰,而且只是稍微快一点(至少在 i7 上):
v + mask ^ mask
: 2.0844380704220384 abs/ns
(v ^ mask) - mask
: 2.0819764093030244 abs/ns
Math.abs
: 2.2636355843860656 abs/ns
这是一个测试,证明它适用于整个整数范围(测试在 Java 7 update 51 下的 i7 处理器上运行不到 2 分钟):
package test;
import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.Test;
public class AbsTest
@Test
public void test()
long processedCount = 0L;
long numberOfIntegers = 1L << Integer.SIZE; //4294967296L
int value;
for (value = Integer.MIN_VALUE; processedCount < numberOfIntegers; value++)
Assert.assertEquals((long) abs(value), (long) StrictMath.abs(value));
if (processedCount % 1_000_000L == 0L)
System.out.print(".");
processedCount++;
System.out.println();
Assert.assertThat(processedCount, Is.is(numberOfIntegers));
Assert.assertThat(value - 1, Is.is(Integer.MAX_VALUE));
private static int abs(int v)
int mask = v >> Integer.SIZE - 1;
return v + mask ^ mask;
【讨论】:
【参考方案5】:这个问题可以分为两个简单的步骤:
1. 如果 >= 0 则只返回数字。
2. 如果小于 0(即负数),则翻转表示该数字为负数的第一位。这可以通过带有 -1 和数字的 XOR 操作轻松完成;然后只需添加 +1 来处理偏移量(有符号整数从 -1 而不是 0 开始)。
public static int absolute(int a)
if (a >= 0)
return a;
else
return (a ^ -1) + 1;
【讨论】:
以上是关于如何使用位操作在java中获取数字的绝对值的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 64 位浏览器和 64 位 java 插件在 64 位 Linux 上获取 32 位 JRE 路径