我想在java中实现一个函数来获取一个数字的绝对值:如果它是正数则不做任何事情,如果它是负数,则转换为正数。
我只想使用位操作而不是数字比较器来做到这一点。
请帮忙
我想在java中实现一个函数来获取一个数字的绝对值:如果它是正数则不做任何事情,如果它是负数,则转换为正数。
我只想使用位操作而不是数字比较器来做到这一点。
请帮忙
好吧,否定:
-n
与二进制补码相同:
~n + 1
问题在于,如果值 < 0,您只想否定。您可以通过使用逻辑移位来查看是否设置了 MSB:
n >>> 31
补码与全 1 的 XOR 相同,类似于(对于 4 位整数):
~1010 == 1010 ^ 1111
我们可以通过算术右移得到一个掩码:
n >> 31
绝对值 说:
因此,将它们放在一起我们可以执行以下操作:
static int abs(int n) {
return (n ^ (n >> 31)) + (n >>> 31);
}
计算:
我不确定是否有一种简单的方法可以在不添加的情况下做到这一点。加法涉及任意数量的进位,即使是简单的增量。
例如 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
微不足道的。相比之下,实际上循环展开完全糟糕。我猜只是好奇。这是我的基准:
数学.abs:4.627323150634766ns 移位/异或/添加绝对值:6.729459762573242ns 循环绝对值:12.028789520263672ns 展开 abs:32.47122764587402ns 位黑客 abs:6.380939483642578ns
(bit hacks abs 是这里显示的非专利技术,它与我的想法基本相同,只是有点难以理解。)
您可以通过取逻辑否定来将二进制恭维数变为正数或负数
i = ~i; // i equals not i
您可以使用该Math.max()
功能始终获得正面
public static int abs(int i) {
return Math.max(i,~i);
}
这取决于您使用的号码类型。对于 int,使用
int sign = i >> 31;
这得到符号位,正数为 0,负数为 1。对于其他基元类型,将 31 替换为基元使用的位数减 1。
然后,您可以在 if 语句中使用该符号。
if (sign == 1)
i = ~i + 1;
我想你会发现这个小曲就是你要找的:
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;
}
}
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;
}
}