如何在java中使用位操作获得数字的绝对值
我想在java中实现一个函数来获取一个数字的绝对值:如果它是正的,什么也不做,如果它是负的,转换成正的 我只想使用位操作,不使用数字比较器如何在java中使用位操作获得数字的绝对值,java,bit-manipulation,absolute-value,Java,Bit Manipulation,Absolute Value,我想在java中实现一个函数来获取一个数字的绝对值:如果它是正的,什么也不做,如果它是负的,转换成正的 我只想使用位操作,不使用数字比较器 请帮助这取决于您使用的号码类型。对于int,使用 int sign = i >> 31; 这将获取符号位,正数为0,负数为1。对于其他基元类型,将31替换为基元使用的位数减去1 然后可以在if语句中使用该符号 if (sign == 1) i = ~i + 1; 你可以通过取一个2的赞美数字的逻辑否定来将它变成正数或负数 i = ~i
请帮助这取决于您使用的号码类型。对于int,使用
int sign = i >> 31;
这将获取符号位,正数为0,负数为1。对于其他基元类型,将31替换为基元使用的位数减去1
然后可以在if语句中使用该符号
if (sign == 1)
i = ~i + 1;
你可以通过取一个2的赞美数字的逻辑否定来将它变成正数或负数
i = ~i; // i equals not i
您可以使用Math.max()
函数始终获得正值
public static int abs(int i) {
return Math.max(i,~i);
}
那么一个否定:
-n
与两者的补语相同:
~n + 1
问题是,只有当值小于0时,才需要求反。通过使用逻辑移位来查看是否设置了MSB,您可以发现:
n >>> 31
补码与所有1的XOR相同,类似于(对于4位整数):
我们可以用算术右移得到一个掩模:
n >> 31
绝对值表示:
- 如果n<0,则取反(取补码并加1)
- 否则,什么也不要做
static int abs(int n) {
return (n ^ (n >> 31)) + (n >>> 31);
}
其中计算:
- 如果n小于0,则将其与所有1进行异或运算,并向其中添加1
- 否则,将其与所有0进行异或运算,并向其中添加0
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;
}
static int abs(int n){
int s=n>>>31;
n^=n>>31;
INTC;
做{
c=(n&s)>>31;
n^=n>>31;
int c=(n&s)我想你会发现这首小曲正是你想要的:
int abs(int v) {
int mask = v >> Integer.SIZE - 1;
return v + mask ^ mask;
}
它基于绝对值方程,不使用比较运算。如果不允许使用加法,则(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分钟):
封装测试;
导入org.hamcrest.core.Is;
导入org.junit.Assert;
导入org.junit.Test;
公开课{
@试验
公开无效测试(){
长处理计数=0L;
长numberOfIntegers=1L>Integer.SIZE-1;
返回v+掩码^mask;
}
}
此问题可分为两个简单步骤:
1.
如果>=0,则只返回数字
2.
如果小于0(即负),则翻转指示数字为负的第一位。这可以通过使用-1和数字的异或运算轻松完成;然后简单地添加+1以处理偏移量(有符号整数从-1开始,而不是从0开始)
我想第一部分,我只是想知道I=-I
的按位替代方案,其实没有。你可以做I=~I+1,但这仍然不是完全按位的。很好的解决方案。我的解决方案仍然需要一个比较。谢谢。我想没有maskforgiggles是没有办法的,我添加了一个没有添加的版本。请注意将Math.abs(Integer.MIN\u VALUE)
生成-2147483648
。
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: 4.627323150634766ns
shift/xor/add abs: 6.729459762573242ns
loop abs: 12.028789520263672ns
unrolled abs: 32.47122764587402ns
bit hacks abs: 6.380939483642578ns
int abs(int v) {
int mask = v >> Integer.SIZE - 1;
return v + mask ^ mask;
}
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;
}
}
public static int absolute(int a) {
if (a >= 0) {
return a;
} else {
return (a ^ -1) + 1;
}
}