首页 新闻 会员 周边

Java的移位问题?

0
悬赏园豆:140 [已解决问题] 解决于 2012-09-13 10:36

程序的目的是:把一个小于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);
}
}

问题补充:

在移位操作的时候,Java会把byte型数据自动转化为int型,<<左移是低位补零,>>右移是符号位扩展

斗榖於菟的主页 斗榖於菟 | 初学一级 | 园豆:60
提问于:2012-09-12 11:27
< >
分享
最佳答案
0

把ctrlbyte3、ctrlbyte4转换成int后再 >> 1 << 7.

收获园豆:100
Launcher | 高人七级 |园豆:45045 | 2012-09-12 11:36

不行呀,还是不一致,Java是会自动转换的。

斗榖於菟 | 园豆:60 (初学一级) | 2012-09-12 11:40

低字节:byte3 不要移位,直接 & 0xff

高字节:byte4 >> 8

byte3 & 0xff | (byte4 & 0xff << 8)

Launcher | 园豆:45045 (高人七级) | 2012-09-12 11:52

@斗榖於菟: 把 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 | 园豆:45045 (高人七级) | 2012-09-12 12:04

@Launcher: 非常感谢你的回答,我的这个移位操作是必须进行的,byte3左移一位,我最想知道的是我的代码为啥会出错?这样就好改了。

斗榖於菟 | 园豆:60 (初学一级) | 2012-09-12 12:32

@斗榖於菟:  数据丢失了,具体的原因,我建议你自己找8个杯子,在其中的某些杯子里放1个鸡蛋,然后你来演示 << 1 后,然后 >> 1 ,能否还原。

Launcher | 园豆:45045 (高人七级) | 2012-09-12 12:44

@Launcher: 我的数字是不大于0x7FFF的,byte3左移&0xFF时,是低位补零,取了低七位,byte4右移&0xFF是取高八位,不会有数据丢失呀。

斗榖於菟 | 园豆:60 (初学一级) | 2012-09-12 13:15

@斗榖於菟: 虽然你的 int 是不大于 0x7FFF,但是你这一左移,第 8 位就被你移到 第 9 位了,然后你再 & 0xff,原先第 8 位的信息就丢失了。当你反向操作时,右移 1 位,第8位就会以 0 填充,而原来第 8 位不一定是 0 。

Launcher | 园豆:45045 (高人七级) | 2012-09-12 13:33

@Launcher: 还有Java中有unsigned char类型的数据吗?

斗榖於菟 | 园豆:60 (初学一级) | 2012-09-12 13:38

@斗榖於菟: 

C++ 中的定义:

typedef unsigned char       BYTE

 

把 unsigned char 替换成你的 byte(如果java跟C#一样有这个关键字的话),它表示的就是一个字节。

Launcher | 园豆:45045 (高人七级) | 2012-09-12 13:44

@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 );
}

}

斗榖於菟 | 园豆:60 (初学一级) | 2012-09-12 16:25

@斗榖於菟: 这样吧,我先问你几个需求的问题:

1,小于0x7FFF 数,包括负数吗?

2,能够拆分成两个 1  字节数,两个 1 字节数合并后能得到原数。

3,左移/右移 1 位 是要解决什么问题?

Launcher | 园豆:45045 (高人七级) | 2012-09-12 16:36

@Launcher:
1.不包括负数;
2.对的,就是要让一个int型数据能存在两个字节里,也能从两个字节中还原这个int型数据;
3.左移一位为了让他的最低位为零,右移是就是把从此高位开始的八位移到一个字节里;

斗榖於菟 | 园豆:60 (初学一级) | 2012-09-12 17:13

@斗榖於菟: 如果你是想存两个字节,还能还原,又是正数:

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 ?

Launcher | 园豆:45045 (高人七级) | 2012-09-12 17:23

@斗榖於菟: 当数字小于 0x8000 并且大于等于 0x0000 时,该数字只有15位有效位,因此按照你的算法: 1, << 1 ,可以把 1 - 7 位保存在 ctrlbyte3 中; 2, >> 7 ,可以把 8 - 15 位保存在 ctrlbyte4 中。这样,所有的有效位 1 - 15 都保留了下来,因此你的算法可以还原。但是,如果数字大于等于 0x8000就不行了,因为有效位数字有 16 位。

所以,你的代码应该没有错误,可能是你给的代码隐藏在一个更大的示例中造成了错误。你可以把该段代码单独拿出来测试下。

另外,可以这么测试,比如 0x3773 无法还原,你可以把移位得到的 ctrlbyte3和 ctrlbyte4的值贴出来,我们先来看看拆开后的值对不对。

Launcher | 园豆:45045 (高人七级) | 2012-09-12 17:44
其他回答(1)
0

你的代码我放到C#里面执行,完全正常,基本无需改动即可运行,也可以正常还原,那么唯一的可能性就是你在“问题补充”里面说的可能不正确

试试这样呢:

private int getRecvCounter() {
return (((int)ctrlbyte3) >> 1) + (((int)ctrlbyte4) << 7);
}

重装过系统,java的环境暂时没时间配置,你自己先试试。

收获园豆:40
秦楼东 | 园豆:913 (小虾三级) | 2012-09-12 14:16

试过了不行,头疼呀~

支持(0) 反对(0) 斗榖於菟 | 园豆:60 (初学一级) | 2012-09-12 14:24

@斗榖於菟: 

特意安装了JAVA的环境测试了下,终于发现问题的原因了。

因为Java的byte是带符号的,Java中没有不带符号的byte,因此在到int的转换时自动补1,所以必须这样处理:

    private int getRecvCounter()
    {
        return ((ctrlbyte3 & 0xff) >> 1) + ((ctrlbyte4&0xff) << 7);
    }
支持(0) 反对(0) 秦楼东 | 园豆:913 (小虾三级) | 2012-09-13 10:38
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册