最近头脑发热,想实现一下不需要if、else语句就能计算出一个数绝对值的算法:
位运算的基本辅助算法:
public static final long MAX_64 = 0xFFFFFFFFFFFFFFFFL;
public static final int MAX_32 = 0xFFFFFFFF;
public static final long FULL_0 = MAX_64 - 1;
public static void main(String args[])
{
System.out.println(abs(354359));
printBin(3, 16);
}
/**设置数字上某一位的数值为0*/
public static long setBin_0(long data,int index)
{
return ~(1 << index) & data;
}
/**设置数字上某一位的数值为1*/
public static long setBin_1(long data,int index)
{
return 1 << index | data;
}
/**获取数字上某一位的数值*/
public static long getBin(long data,int index)
{
return data >> index & 1;
}
/**以二进制方式打印数字*/
public static String printBin(long num,int count)
{
String str = "";
for (int i = count-1; i >= 0; i--)
{
str += getBin(num,i);
if(i % 8==0 && i != 0)
{
str += ",";
}
else if(i % 4==0)
{
str += " ";
}
}
System.out.println(str);
return str;
}
设计思路:
首先明白,数字在计算机上是以二进制存储的,正数(无符号数)、负数(有符号数)的区别在于最高位上是否为1,而且正数储存方式为原码、负数是以补码的方式储存的。最后就是通过符号位,对数字进行筛选。
int num = 5443;
正数的二进制原码表示 0000 0000,0000 0000,0001 0101,0100 0011
int num = -5443;
负数的二进制原码表示 1000 0000,0000 0000,0001 0101,0100 0011
↓
负数的二进制反码表示 1111 1111,1111 1111,1110 1010,1011 1100
↓
负数的二进制补码表示 1111 1111,1111 1111,1110 1010,1011 1101 (在反码的基础上+1)
1、可以看到一个数的负数形式,是通过这个数的正数,经过符号位变成1,然后'原码'>‘反码’>‘补码’的过程转换的。那么将一个负数装成正数就是这个过程的逆过程。
2、要注意的是,我们不能使用if、else来判断(这样的? :三元运算符也不行),那么我们的算法就要求能够适应正数、负数的“一波流”了!
不是我耍赖,我们先用if、else的写法来演示一下,这样更加清晰:
public static long absIf_else(long num)
{
byte signBit = (byte)getBin(num,63);//获取符号位
if(signBit == 1)//负数
{
num -= 1;
num ^= MAX_64;//取反
}
else//否则不做任何事
{
}
return num;
}
测试通过!
System.out.println(absIf_else(-354359));//354359
System.out.println(absIf_else(354359));//354359
然后再把算法变成一波流!
看代码:
if(signBit == 1)//负数
{
num -= 1;
num ^= MAX_64;//取反
}
1、num -= 1;我们可以用num -= signBit代替,因为如果是正数signBit=0,减去0当然是保持不变的。
2、num ^= MAX_64;可以把MAX_64*signBit,因为如果是正数signBit=0,相乘等于0,原数与0异或运算保持不变。
所以一波流的算法如下:
byte signBit = (byte)getBin(num,63);//符号位
num -= signBit;//补码转反码
num ^= (MAX_64 * signBit);//通过异或运算、反码转原码
实现的函数:
public static long abs(long num)
{
byte signBit = (byte)getBin(num,63);//符号位
num -= signBit;//补码转反码
num ^= (MAX_64 * signBit);//通过异或运算、反码转原码
return num;
}
public static double abs(double floatNum)
{
long num = Double.doubleToLongBits(floatNum);
return Double.longBitsToDouble(setBin_0(num, 63));
}
完毕