views:

253

answers:

7

I'm trying to represent the port number 9876 (or 0x2694 in hex) in a two byte array:

class foo {
     public static void main (String args[]) {
   byte[] sendData = new byte[1];

   sendData[0] = 0x26;
   sendData[1] = 0x94;
     }
}

But I get a warning about possible loss of precision:

foo.java:5: possible loss of precision
found   : int
required: byte
   sendData[1] = 0x94;
                 ^
1 error

How can I represent the number 9876 in a two byte array without losing precision?

NOTE: I selected the code by @Björn as the correct answer, but the code by @glowcoder also works well. It's just a different approach to the same problem. Thank you all!

+4  A: 

Have you tried casting to a byte ? e.g.

sendData[1] = (byte)0x94;
Brian Agnew
That will cause me to loose precision, won't it?
Mark Roberts
@Mark: It will make it clear to the compiler that you're doing exactly what you want to do. 0x94 is within the range of a byte, so there's no actual loss of precision here.
Michael Myers
No it is not within the range of a byte. Everything in java is signed, and 0x94 is read as an int, and casted to a byte.
glowcoder
I thought the range of a byte was from -128 to 127, and 0x94 is 148
Mark Roberts
@Mark - 0x94 as a byte is not actually 148, it's -108.
nsayer
@Mark: Well... no. Looking at the bits, the range of a byte is 0b00000000 to 0b11111111, and 0x94=0b10010100, which is properly in its range. Bitwise, the concept "signed byte" doesn't make any sense. It's only when you interpret a sequence of eight bits as an integer that it starts being clear.
Vuntic
+1  A: 

You have to cast to (byte) as default number type in java is int, which is bigger than byte. As long as the value fits in byte it is ok to cast.

Anton
+1  A: 

Try this:

sendData[0] =(byte)0x26
sendData[1] =(byte)0x94

Or this:

sendData[0] =(byte)38
sendData[1] =(byte)148

You must cast data into byte in order to assign it to a byte!

That does not mean you lost precision, just writing 0x26 means an int to Java compiler..

But also note: range of a byte is from -128 to 127, so in case of 0x94=148 it will be represented after byte casting as '-108' , so it will not work correctly in mathematical computations..

Betamoo
A: 

It's because everything in Java is signed. 0x94 (148) is greater than Byte.MAX_VALUE(2^7-1).

What you need is

public static byte[] intToByteArray(int value) {
    byte[] b = new byte[4];
    for (int i = 0; i < 4; i++) {
        int offset = (b.length - 1 - i) * 8;
        b[i] = (byte) ((value >>> offset) & 0xFF);
    }
    return b;
}
glowcoder
Note, this does all 4 bytes of the int... you only need two of them. The rest is straightforward from this.
glowcoder
that's two wrong--it's not because everything's signed, it's because 0x94 is an int, not a byte, and secondly the char type is unsigned, and thirdly--is that code a joke? If you're just messing with the guy it might be worth a +1 :) the original was fine if you downcast with (byte)
Bill K
0x94 is an int because it doesn't fit into a byte `because bytes are signed`. You're correct that chars are unsigned - however, it's the exception to the rule because it's not `intended` to be a number - the JavaGods decided all their numbers will be signed. Can you use it like a number for String optimization? Yes you can. Was it intended to be used as number? No. Does the `Character` class extend the `Number` class? No. Third, no this code is not a joke, it's a legit way to convert any int to a byte array to accurately represent its bits. I've used it for TCP/IP packet creation in the past.
glowcoder
@glowcoder, I tried your code, and it works great. Thank you for helping a beginner to learn Java!
Mark Roberts
+1  A: 

0x94 is 148 in decimal, which exceeds the range of byte in java (-128 to 127). You can do one of the following:

1) A cast will work fine because it will preserve the binary representation (no meaningful bits are truncated for 0x00 to 0xFF):

 sendData[1] = (byte)0x94; 

2) The binary representation of 0x94 as a signed byte is -108 (-0x6C), so the following will have the same effect:

sendData[1] = -0x6C; //or -108 in decimal
Eyal Schneider
+1  A: 

My first answer would be bitshifting, but on a second thought I think using outputstreams could be better and more simple to understand. I usually avoid casting, but if you're not going for a generic solution I guess that would be okay. :)

Using streams, a generic solution:

public byte[] intToByteArray(final int i) throws java.io.IOException {
    java.io.ByteArrayOutputStream b = new java.io.ByteArrayOutputStream();
    java.io.DataOutputStream d = new java.io.DataOutputStream(b);
    d.writeInt(i);
    d.flush();

    return b.toByteArray();
}

And to reverse it:

public int byteArrayToInt(final byte[] b) throws IOException {
    java.io.ByteArrayInputStream ba = new java.io.ByteArrayInputStream(b);
    java.io.DataInputStream d = new java.io.DataInputStream(ba);

    return d.readInt();
}
Björn
You can also do this generically with java.nio.ByteBuffer and also control endianess if necessary. I've added an answer for this possibility.
Kevin Brock
A: 

Björn gave a good generic answer with using streams. You can also do this same thing using java.nio.ByteBuffer which results in slightly less code and you could also control endianess (byte order) of the output.

To create the byte array:

public static byte[] toByteArray(int bits) {
    ByteBuffer buf = ByteBuffer.allocate(4);
    buf.putInt(bits);
    return buf.array();
}

To reverse it:

public static int fromByteArray(byte[] b) {
    ByteBuffer buf = ByteBuffer.wrap(b);
    return buf.getInt();
}
Kevin Brock