SO, I'm playing around with an idea. I'm trying to convert primitives to and from a char array for file logging. I've got all the details worked out, but I don't understand something.. Take the following case, for example:

public static byte[] ToByta (short data) {
	return new byte[]{
		(byte)(data & 0xff),
		(byte)((data >>> 8) & 0xff)
	};
}

The above converts a short value to a 2-byte byte array using bitwise operators.

Let's test it out:
0 yields {0, 0}
10 yields {10, 0}
-10 yields {-10, -1}
127 yields {127, 0}
128 yields {-128, 0}
1234 yields {-46, 4}
-1234 yields {46, -5}

Everything looks normal.
Now, lets pass those arrays through the original reverting function:

public static short ToShort (byte[] data) {
	if (data == null) return 0x0;
	if (data.length != 2) return 0x0;
	return (short) (
		data[0]        |
		data[1] << 8
	);
}

Can you see anything wrong with that? Maybe.

Let's test it out:
0 yields 0
10 yields 10
-10 yields -10
127 yields 127
128 yields -128
1234 yields -46
-1234 yields 1234

As you can see, there's a few issues. I knew it had something to do with the bits, so I played around a bit and came up with this:

public static short ToShort (byte[] data) {
	if (data == null) return 0x0;
	if (data.length != 2) return 0x0;
	return (short) (
		0xff           &
		data[0]        |
		data[1] << 8
	);
}

Which seems to work perfectly.
All I added was the 0xff & in the combination sequence.. And I don't know why this works whereas it doesn't work without the 0xff & .

Can someone please explain that to me?

10101010

does not equal

11111111 &
10101010

??? :-|

Recommended Answers

All 9 Replies

Long post, I know, but I just discovered:
Likewise, for the chararray-to-int method:
(it doesn't work properly any other way. Try 2147483647 as the value.)

public static int ToInt (byte[] data) {
	if (data == null) return 0x0;
	if (data.length != 4) return 0x0;
	return (
		(0xff & data[0])        |
		(0xff & data[1]) << 8   |
		(0xff & data[2]) << 16  |
		data[3] << 24
	);
}

Here's conversion methods for short, low byte given first in array.

public static byte[] toBytes(short s)
    {
        return new byte[]{(byte)(s & 0x00FF),(byte)((s & 0xFF00)>>8)};
    }
    
    public static short toShort(byte[] b)
    {
        return (short)(b[1]<<8 | b[0]);
    }

why not just log them as they are and use the methods in the wrapper classes to convert them back into primitive numbers?

why not just log them as they are and use the methods in the wrapper classes to convert them back into primitive numbers?

BECAUSE, that's not fun.
I "play" with Java to learn.
I know there's an easy way around everything, and if this were my job, I'd probably take the easy way. I, however, like to learn, and I like to know how things work. I want to convert the primitives to byte arrays, and I've done so. I just didn't understand *exactly* where my problems arise.

:cool:

Anyway, I found my problem just moments ago. Performing shift operations on anything converts it to an integer. The only remaining issue I was having was with char-array-to-long conversion, and it was because I wasn't converting certain things to long before working with them.. See the before and after, below. Maybe others can learn from this:

// BEFORE (Does Not Work for negatives and large longs)
public static long ToLong (byte[] data) {
	if (data == null) return 0x0;
	if (data.length != 8) return 0x0;
	return (long) ( // TODO: issues.
		(0xff & data[0])        |
		(0xff & data[1]) << 8   |
		(0xff & data[2]) << 16  |
		(0xff & data[3]) << 24  |
		(0xff & data[4]) << 32  |
		(0xff & data[5]) << 40  |
		(0xff & data[6]) << 48  |
		(0xff & data[7]) << 56
	);
}

// AFTER (Works for all longs)
public static long ToLong (byte[] data) {
	if (data == null) return 0x0;
	if (data.length != 8) return 0x0;
	return (long) ( // TODO: issues.
		(0xff & data[0])        |
		(0xff & data[1]) << 8   |
		(0xff & data[2]) << 16  |
		(0xff & data[3]) << 24  |
		(long)(0xff & data[4]) << 32  |
		(long)(0xff & data[5]) << 40  |
		(long)(0xff & data[6]) << 48  |
		(long)(0xff & data[7]) << 56
	);
}

:p There ya go! Conversions. I've learned something.

I think you need one more long cast in the toLong on the << 24 line. Here are all the functions that I put together for writing and reading including float and double. One thing I just noticed is that you have to be careful to write the correct type. Meaning, if you write an int and try to read a float, it doesn't work. so, be sure to write 1f rather than 1 for example.

public static void write(byte[] bytes, boolean b, int pos){
		bytes[pos] = (byte) (b ? 1 : 0);
	}
	
	public static void write(byte[] bytes, short s, int pos){		
		bytes[pos] = (byte)( (s >>> 8) & 0xFF );
		bytes[pos+1] = (byte)( (s >>> 0) & 0xFF);
	}
	
	public static void write(byte[] bytes, int i, int pos){		
		bytes[pos] = (byte)( (i >>> 24) & 0xFF );
		bytes[pos+1] = (byte)( (i >>> 16) & 0xFF);
		bytes[pos+2] = (byte)( (i >>> 8) & 0xFF);
		bytes[pos+3] = (byte)( (i >>> 0) & 0xFF);
	}
	
	public static void write(byte[] bytes, long l, int pos){		
		bytes[pos] = (byte)( (l >>> 56) & 0xFF );
		bytes[pos+1] = (byte)( (l >>> 48) & 0xFF);
		bytes[pos+2] = (byte)( (l >>> 40) & 0xFF);
		bytes[pos+3] = (byte)( (l >>> 32) & 0xFF);
		bytes[pos+4] = (byte)( (l >>> 24) & 0xFF);
		bytes[pos+5] = (byte)( (l >>> 16) & 0xFF);
		bytes[pos+6] = (byte)( (l >>> 8) & 0xFF);
		bytes[pos+7] = (byte)( (l >>> 0) & 0xFF);
	}
	
	public static void write(byte[] bytes, float f, int pos){
		write(bytes, Float.floatToIntBits(f), pos);
	}
	
	public static void write(byte[] bytes, double d, int pos){
		write(bytes, Double.doubleToLongBits(d), pos);
	}
	
	public static boolean readBoolean(byte[] bytes, int pos){
		return bytes[pos] == 1;
	}
		
	public static short readShort(byte[] bytes, int pos){		
		return (short)( ( (bytes[pos] << 8) ) | ( (bytes[pos+1] & 0xff) ) );
	}
	
	public static int readInt(byte[] bytes, int pos){
		return (int) (
			(0xff & bytes[pos + 3]) |
			(0xff & bytes[pos + 2]) << 8 |
			(0xff & bytes[pos + 1]) << 16 |
			(bytes[pos]) << 24			
		);
	}
	
	public static long readLong(byte[] bytes, int pos){
		return (long) (			
			(0xff & bytes[pos + 7]) |
			(0xff & bytes[pos + 6]) << 8 |
			(0xff & bytes[pos + 5]) << 16 |
			(long)(0xff & bytes[pos + 4]) << 24 |
			(long)(0xff & bytes[pos + 3]) << 32 |
			(long)(0xff & bytes[pos + 2]) << 40 |
			(long)(0xff & bytes[pos + 1]) << 48 |
			(long)(0xff & bytes[pos]) << 56
		);
	}
	
	public static float readFloat(byte[] bytes, int pos){
		return Float.intBitsToFloat( readInt(bytes, pos) );
	}
	
	public static double readDouble(byte[] bytes, int pos){
		return Double.longBitsToDouble( readLong(bytes, pos) );
	}

Bit late don't you think, 17 months after the last message?

This thread appears to be created for the purposes of "playing around with an idea". I found it useful for my thesis so figured I'd post the implementation that I'm using to potentially save people time, and point out a small bug in the implementation. I imagine it will be more useful over the next 17 months than your criticism.

Hi ekirkco,

Can I just ask why you use a signed shift in your write methods? I'm just wondering because I saw this in another example on another forum but it wasn't explained and I had trouble when sending data to another computer. I ended up using an unsigned shift and it worked, but I don't know why...

The bitwise functions only operate on integers. When you wrote

    data[0]        |
    data[1] << 8

it didn't work as you thought when data contained {-128, 0}

-128 as a byte is 10000000
-128 as an int is 11111111111111111111111110000000 (25 1s, 7 0s)

the int is what is being used in the calculation, thus your data is all screwed up

thats why you use the "& 0xff" it condenses the 111...110000000 to what you originally wanted

in case you're confused, the leftmost bit is the sign bit

1 for negative numbers
0 for positive numbers

But its more confusing then just that, here is the rule for switching signs

positive to negative: switch the digits and add 1
negative to positive: subtract 1 then switch the digits

also there's a rule when moving from a smaller type to a larger type

just keep the original binary and fill the open spots on the left with the leftmost digit
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.