文章背景
最近写了一次涉及与或非很多的逻辑:为了减轻网络数据量,我们接受丧失部分精度,需要把一堆float数据根据可以接受丧失精度的程度转成byte或者short,然后通过网络发出去,回来之后再转成float数组提供使用。所以是一个双向的转换操作。
之前一直对java中的int没什么概念,C/C++中有unsigned的概念,java中没有,所以导致java来做这种纯数据交换不涉及符号的操作的时候很费劲。
我们知道C++里面:
-
char
, 范围至少为 [-127 ~ 127] 。 (一般范围是 -128 到 127) -
unsigned char
, 范围至少为 [0 ~ 255]。
而java里面的byte也是占一个字节,相当于C++里面的char,范围是[-128,127]
,当然这是十进制的大小范围,转化成二进制后,这个大小应该怎么算呢?
带符号的数组,符号位在最高位,也就是说如果转化成二进制,负数是大于正数的。
十进制的大小顺序应该是[-128-> -1 -> 0 -> 127]
二进制的大小顺序应该是[0 -> 127 -> -128 -> -1]
int => 四个byte
一个int可以拆解成四个byte,直接右移强转就可以,强转成byte的时候会直接取低8位的数据。例子代码如下:
int i;
byte b0 = (byte) (i >>> 24);
byte b1 = (byte) (i >>> 16);
byte b2 = (byte) (i >>> 8);
byte b3 = (byte) (i);
四个byte组合成一个int
四个byte组成int,需要注意的是byte的顺序,是按照大端还是小端存储。作为数据传输来讲,大端是比较合适的,如果用C++来写,大端就可以直接按照offest添加就可以了。
其实跟int拆解成4个byte思路是类似的。也是用 左移 + 与操作
就可以实现。使用位移分别把4个byte放到对应的位置上,然后使用与操作拼成一个int。
int x = b0 << 24 | b1 << 16 | b2 << 8 | b3 ;
假如 b0 = 59, b1 = 47, b2 = 123, b3 = 120;
b0 << 24 => 00111011 00000000 00000000 00000000
b1 << 16 => 00000000 00101111 00000000 00000000
b2 << 8 => 00000000 00000000 01111011 00000000
b3 << 0 => 00000000 00000000 00000000 01111000
x => 00111011 00101111 01111011 01111000
这样看起来是没有问题的,但如果b3的值不是120,而是 -120,我们再来看一下b3的二进制表示:
-120 => 11111111 11111111 11111111 10001000
问题就来了,如果有一个值是负数,高位的负数会冲掉原来与上的值。
所以我们需要下面这样的处理:
int x = b0 << 24 & 0xFF000000 | b1 << 16 & 0xFF0000 | b2 << 8 & 0xFF00 | b3 & 0xFF;
//或者是
int x_ = (b0 & 0xFF) << 24 | (b1 & 0xFF) << 16 | (b2 & 0xFF) << 8 | b3 & 0xFF;
只留下我们需要的位,其他的位都做成0,不影响结果就好。
float => byte | float => short
从一个归一化到0到1之间的float得到byte表示:
/**
* 归一化的float 转化为byte
*/
private static byte getByte(float f) {
return (byte) (f * (Byte.MAX_VALUE - Byte.MIN_VALUE));
}
/**
* 归一化的float转化为short,分解成两个byte
*/
private static byte[] getShort(float f) {
short s = (short) (f * (Short.MAX_VALUE - Short.MIN_VALUE));
//右移8位获得高8位
byte[] bytes = new byte[2];
bytes[0] = (byte) (s >> 8);
//获得低8位
bytes[1] = (byte) (s);
return bytes;
}
byte => float | short => float
short或者byte转回float:
/**
* short转化为归一化的float
*/
private static float getFloatFromShort(short t) {
float f;
int scope = Short.MAX_VALUE - Short.MIN_VALUE;
if (t > 0) {
//符号位为0,正数,直接除
f = ((float) t) / scope;
} else if (t < 0) {
//符号位为1,负数,加Short.MIN
f = ((float) t + scope) / scope;
} else {
f = 0;
}
return f;
}
/**
* byte转化为归一化的float
*/
private static float getFloatFromByte(byte b) {
float f;
int scope = Byte.MAX_VALUE - Byte.MIN_VALUE;
if (b > 0) {
//符号位为0,正数,直接除
f = ((float) b) / scope;
} else if (b < 0) {
//符号位为1,负数,Byte.MIN
f = ((float) b + scope) / scope;
} else {
f = 0;
}
return f;
}
网友评论