程序的目的是:把一个小于0x7FFF的数存放在两个byte字节里(setRecvcounter方法);
需要时从两个byte字节中还原这个int型数据(getRecvcounte方法);
问题:
为何这个程序在输出时,有时是对的有时不对,比如counter=0x1234,可以还原,counter=0x3773,无法还原,问题出在哪里?
class Test {
byte ctrlbyte3;
byte ctrlbyte4;
public static void main(String[] args) {
int counter=0x7fff;
Test a=new Test();
a.setRecvCounter(counter);
System.out.println("counter = "+counter);
System.out.println("a.getRecvCounter() = "+a.getRecvCounter());
}
private int getRecvCounter() {
return (ctrlbyte3 >> 1) + (ctrlbyte4 << 7);
}
private void setRecvCounter(int recvcounter) {
ctrlbyte3 = (byte) ((recvcounter << 1) & 0xff);
ctrlbyte4 = (byte) ((recvcounter >> 7) & 0xff);
}
}
把ctrlbyte3、ctrlbyte4转换成int后再 >> 1 << 7.
不行呀,还是不一致,Java是会自动转换的。
低字节:byte3 不要移位,直接 & 0xff
高字节:byte4 >> 8
byte3 & 0xff | (byte4 & 0xff << 8)
@斗榖於菟: 把 unsigned char 替换成泥的 byte。
int recvcounter = 0x3773;
unsigned char ctrlbyte3;
unsigned char ctrlbyte4;
ctrlbyte3 = (unsigned char) ((recvcounter) & 0xff);
ctrlbyte4 = (unsigned char) ((recvcounter >> 8) & 0xff);
int counter = ((int)(((unsigned char)(((int)(ctrlbyte3)) & 0xff)) | ((int)((unsigned char)(((int)(ctrlbyte4)) & 0xff))) << 8));
@Launcher: 非常感谢你的回答,我的这个移位操作是必须进行的,byte3左移一位,我最想知道的是我的代码为啥会出错?这样就好改了。
@斗榖於菟: 数据丢失了,具体的原因,我建议你自己找8个杯子,在其中的某些杯子里放1个鸡蛋,然后你来演示 << 1 后,然后 >> 1 ,能否还原。
@Launcher: 我的数字是不大于0x7FFF的,byte3左移&0xFF时,是低位补零,取了低七位,byte4右移&0xFF是取高八位,不会有数据丢失呀。
@斗榖於菟: 虽然你的 int 是不大于 0x7FFF,但是你这一左移,第 8 位就被你移到 第 9 位了,然后你再 & 0xff,原先第 8 位的信息就丢失了。当你反向操作时,右移 1 位,第8位就会以 0 填充,而原来第 8 位不一定是 0 。
@Launcher: 还有Java中有unsigned char类型的数据吗?
@斗榖於菟:
C++ 中的定义:
typedef unsigned char BYTE
把 unsigned char 替换成你的 byte(如果java跟C#一样有这个关键字的话),它表示的就是一个字节。
@Launcher: 按照你分析的错误原因,改完之后还是不对,代码如下:
class Test {
byte ctrlbyte3;
byte ctrlbyte4;
public static void main(String[] args) {
int counter;
Test a = new Test();
for (counter = 0; counter <= 0x7fff; counter++) {
a.setRecvCounter(counter);
if (counter != a.getRecvCounter()) {
System.out.println("counter = " + counter);
System.out
.println("a.getRecvCounter() = " + a.getRecvCounter());
}
}
System.out.println("checking end");
}
private int getRecvCounter() {
return ( ( ((int) ctrlbyte3 ) >> 1) & 0x7f + (((int) ctrlbyte4 ) << 7) & 0x7f00 );
}
private void setRecvCounter(int recvcounter) {
ctrlbyte3 = (byte) ( ( ( (int) (recvcounter) ) & 0xff ) << 1 );
ctrlbyte4 = (byte) ( ( ( (int) (recvcounter) ) & 0xff ) >> 7 );
}
}
@斗榖於菟: 这样吧,我先问你几个需求的问题:
1,小于0x7FFF 数,包括负数吗?
2,能够拆分成两个 1 字节数,两个 1 字节数合并后能得到原数。
3,左移/右移 1 位 是要解决什么问题?
@Launcher:
1.不包括负数;
2.对的,就是要让一个int型数据能存在两个字节里,也能从两个字节中还原这个int型数据;
3.左移一位为了让他的最低位为零,右移是就是把从此高位开始的八位移到一个字节里;
@斗榖於菟: 如果你是想存两个字节,还能还原,又是正数:
ctrlbyte3 = (unsigned char) ((recvcounter) & 0xff);
ctrlbyte4 = (unsigned char) ((recvcounter >> 8) & 0xff);
int counter = ((int)(((unsigned char)(((int)(ctrlbyte3)) & 0xff)) | ((int)((unsigned char)(((int)(ctrlbyte4)) & 0xff))) << 8));
这个方法不行吗?
为什么需要 ctrlbyte3 的最低位是 0 ?
@斗榖於菟: 当数字小于 0x8000 并且大于等于 0x0000 时,该数字只有15位有效位,因此按照你的算法: 1, << 1 ,可以把 1 - 7 位保存在 ctrlbyte3 中; 2, >> 7 ,可以把 8 - 15 位保存在 ctrlbyte4 中。这样,所有的有效位 1 - 15 都保留了下来,因此你的算法可以还原。但是,如果数字大于等于 0x8000就不行了,因为有效位数字有 16 位。
所以,你的代码应该没有错误,可能是你给的代码隐藏在一个更大的示例中造成了错误。你可以把该段代码单独拿出来测试下。
另外,可以这么测试,比如 0x3773 无法还原,你可以把移位得到的 ctrlbyte3和 ctrlbyte4的值贴出来,我们先来看看拆开后的值对不对。
你的代码我放到C#里面执行,完全正常,基本无需改动即可运行,也可以正常还原,那么唯一的可能性就是你在“问题补充”里面说的可能不正确
试试这样呢:
private int getRecvCounter() { return (((int)ctrlbyte3) >> 1) + (((int)ctrlbyte4) << 7); }
重装过系统,java的环境暂时没时间配置,你自己先试试。
试过了不行,头疼呀~
@斗榖於菟:
特意安装了JAVA的环境测试了下,终于发现问题的原因了。
因为Java的byte是带符号的,Java中没有不带符号的byte,因此在到int的转换时自动补1,所以必须这样处理:
private int getRecvCounter() { return ((ctrlbyte3 & 0xff) >> 1) + ((ctrlbyte4&0xff) << 7); }