2010年1月29日星期五

Java中无符号数的处理

Java中int,short,byte都是有符号的,如果想处理无符号数,只有一个办法,用容量更大的类型替换,例如unsigned short就用int。唯一的例外是char,它本身就是无符号的,所以可以用char作为unsigned short。

那么从一个byte[]中怎么读取一个有符号的数呢?以unsigned short为例:
int unsignedShort ;
byte[]  buf = getFromSomePlace();
int b1 = buf[0];
int b2 = buf[1];

unsignedShort = (((b1 & 0xff) << 8) | (b2 & 0xff))

那么,((b1 & 0xff) << 8)是什么意思呢?
首先, &  操作符会自动提升byte到int。
然后看b1&0xff的含义。大于2^15-1的无符号short,用两位byte表示,最高位为1。因为Java中都是有符号的数,最高位是符号位,为1表示负数。当byte扩充为int时,扩充位全都会变成1,b1 & 0xff就是将高位的1去掉。
最后是位移8位,和下一个byte拼接成unsigned short。

对于unsigned short,其实还可以用char存储:

char unsignedShort = (char) (b1 << 8 | b2);

我们去查看DataInput的javadoc,readUnsignedShort就是这样的。那位同学说了,我怎么去看DataInputStream的实现不是这样的,
public final int readUnsignedShort() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (ch1 << 8) + (ch2 << 0);
}

没有什么&0xff。问题就在in.read()。InputStream的read方法返回的是int,从api描述可以看出:
Reads the next byte of data from the input stream. The value byte is returned as an int in the range 0 to 255.
可以看出,实际上返回的是一个无符号的byte,内部已经做了转换。我们可以看一个具体的实现,ByteArrayInputStream.read()
public int read() throws IOException {
        if (closed) {
            throw new IOException("Stream closed");
        } else if (index >= limit) {
            return -1; // EOF
        } else {
            return buffer[index++] & 0xff;
        }
}

也就是说,Java中没有unsigned byte,InputStreamde read方法都统一返回int,所以DataInputStream中就不用再转换了。

没有评论: