Problem reconstructing decomposed int

Q

Qu0ll

I am trying to decompose an int and then reconstruct it in the simplest way
possible. Can anyone tell me why z and result are completely different in
the following code?

final int z = 1234567890;
final byte a = (byte)(z >> 24);
final byte b = (byte)(z >> 16);
final byte c = (byte)(z >> 8);
final byte d = (byte)z;
final int e = a << 24;
final int f = b << 16;
final int g = c << 8;
final int h = d;
final int result = e + f + g + h;
System.out.println("Compare " + z + " to " + result);


--
And loving it,

-Qu0ll (Rare, not extinct)
_________________________________________________
(e-mail address removed)
[Replace the "SixFour" with numbers to email me]
 
J

Joshua Cranmer

Qu0ll said:
I am trying to decompose an int and then reconstruct it in the simplest
way possible. Can anyone tell me why z and result are completely
different in the following code?

What is z in hexadecimal? It is 0x499602d2, or, decomposing into bytes,
0x49, 0x96, 0x02, 0xd2.

Now, bytes are signed, which means that 0x96, when converted to an
integer, becomes 0xffffff96. I too would like to kill whomever decided
that signed octets are more useful than unsigned octets.
 
M

Mike Schilling

Joshua said:
What is z in hexadecimal? It is 0x499602d2, or, decomposing into
bytes, 0x49, 0x96, 0x02, 0xd2.

Now, bytes are signed, which means that 0x96, when converted to an
integer, becomes 0xffffff96. I too would like to kill whomever
decided
that signed octets are more useful than unsigned octets.

Now, now, I agree that signed byte were a terrible botch, but let's
not consider a quick death sufficient punishment.
 
R

Roedy Green

final int z = 1234567890;
final byte a = (byte)(z >> 24);
final byte b = (byte)(z >> 16);
final byte c = (byte)(z >> 8);
final byte d = (byte)z;
final int e = a << 24;
final int f = b << 16;
final int g = c << 8;
final int h = d;
final int result = e + f + g + h;
System.out.println("Compare " + z + " to " + result);

to solve a problem like this you should either use a trace debug to
see what happens at each step, or pepper your code with out.println to
see the intermediate results.

Don't use + for composition. Use |. See
http://mindprod.com/jgloss/binary.html
http://mindprod.com/jgloss/or.html

Most of the time the problem comes from confusing signed and unsigned
values. Try to think purely in unsigned terms.

see http://mindprod.com/jgloss/unsigned.html

Also see
http://mindprod.com/jgloss/masking.html

BIG HINT: Nearly always when you take something apart you need two
operators, a shift to align it and a & to get rid of the unwanted
stuff surrounding your field.


--
Roedy Green Canadian Mind Products
http://mindprod.com

"Deer hunting would be fine sport, if only the deer had guns."
~ William S. Gilbert of Gilbert and Sullivan
 
R

Roedy Green

Now, now, I agree that signed byte were a terrible botch, but let's
not consider a quick death sufficient punishment.

It would relatively trivial to add an unsigned byte type to Java. You
would not need to change the JVM, just Javac. It would be implemented
by mindlessly tacking on an & 0xff (sipush/iand) after every load.

I don't think I have ever in my life of using Java since 1.0 used a
signed byte.

There might be few spare op codes around to add a bauload
load-unsigned byte from array. Temporary byte variables are ints
anyway, so they can be treated as ints.


--
Roedy Green Canadian Mind Products
http://mindprod.com

"Deer hunting would be fine sport, if only the deer had guns."
~ William S. Gilbert of Gilbert and Sullivan
 
M

Mike Schilling

Roedy said:
It would relatively trivial to add an unsigned byte type to Java.
You
would not need to change the JVM, just Javac. It would be
implemented
by mindlessly tacking on an & 0xff (sipush/iand) after every load.

And even that's needed only when you're going to promote it to a
larger type or do arithmetic on it. It's not needed when passing a
byte as an argument to another method, since the called method will
chop off all but the bottom 8 bits anyway.
 
R

Roedy Green

And even that's needed only when you're going to promote it to a
larger type or do arithmetic on it. It's not needed when passing a
byte as an argument to another method, since the called method will
chop off all but the bottom 8 bits anyway.

No it won't. The stack is 32 bits. parms are 32 bits. Local temps are
32 bits. The all have the high bits on for negative bytes.

baload sign extends. You have to mask that off again to get an int
that is the equivalent of the unsigned byte.

In java you would need byte code instructions baload and the proposed
buaload to load from a byte array signed/unsigned.

A real world CPU might do a load byte three ways:

1. load it in the low byte of a register leaving the rest intact.

2. load it into the low byte of a register and clear the high bytes to
0.

3. load it into low byte of a register and set the high bytes to the
sign bit.

I have not done any 32-bit assembler in such a long time I forget
which option are available on the Pentium.
--
Roedy Green Canadian Mind Products
http://mindprod.com

"Deer hunting would be fine sport, if only the deer had guns."
~ William S. Gilbert of Gilbert and Sullivan
 
Q

Qu0ll

I am trying to decompose an int and then reconstruct it in the simplest
way possible. Can anyone tell me why z and result are completely
different in the following code?

final int z = 1234567890;
final byte a = (byte)(z >> 24);
final byte b = (byte)(z >> 16);
final byte c = (byte)(z >> 8);
final byte d = (byte)z;
final int e = a << 24;
final int f = b << 16;
final int g = c << 8;
final int h = d;
final int result = e + f + g + h;
System.out.println("Compare " + z + " to " + result);

Thanks to all those who replied. I didn't realise it would be so involved
just to do something which seems trivial.

Peter's code works great - thanks.

--
And loving it,

-Qu0ll (Rare, not extinct)
_________________________________________________
(e-mail address removed)
[Replace the "SixFour" with numbers to email me]
 
M

Mike Schilling

Roedy said:
No it won't. The stack is 32 bits. parms are 32 bits. Local temps
are
32 bits. The all have the high bits on for negative bytes.

I see that I left part of that sentence out. It's not needed when
passing a byte as an argument to another method *that expects a byte*.
 
R

Roedy Green

I see that I left part of that sentence out. It's not needed when
passing a byte as an argument to another method *that expects a byte*.

To resolve the matter, lets talk about some actual code.

For example

void display( byte b )
{
out.println( b );
}

is going to print a negative number if the high bit of b is on.

To treat it as unsigned you need

void display( byte b )
{
out.println( b & 0xff );
}

If you look at the byte code generated, I think you will find an int
being pushed to the stack as the parameter value. Under the covers,
byte is treated as an int. (This may be the source of the mystery why
bytes are signed). The only time the byteness comes into play is when
you write code of the form:

b = ba[ i ];

then the sign bit is propagated.

--
Roedy Green Canadian Mind Products
http://mindprod.com

"Deer hunting would be fine sport, if only the deer had guns."
~ William S. Gilbert of Gilbert and Sullivan
 
R

Roedy Green

You can fix the whole thing by masking your shifted values and composing
them with the '|' operator:

final int result2 = (e & 0xff000000) |
(f & 0x00ff0000) |
(g & 0x0000ff00) |
(h & 0x000000ff);

It is easier to do your masking when the values are in the low order
positions. Then you can often reuse the same simple mask, in this case
0xff.

--
Roedy Green Canadian Mind Products
http://mindprod.com

"Deer hunting would be fine sport, if only the deer had guns."
~ William S. Gilbert of Gilbert and Sullivan
 
M

Mike Schilling

Roedy said:
I see that I left part of that sentence out. It's not needed when
passing a byte as an argument to another method *that expects a
byte*.

To resolve the matter, lets talk about some actual code.

For example

void display( byte b )
{
out.println( b );
}

is going to print a negative number if the high bit of b is on.

To treat it as unsigned you need

void display( byte b )
{
out.println( b & 0xff );
}

If you look at the byte code generated, I think you will find an int
being pushed to the stack as the parameter value. Under the covers,
byte is treated as an int. (This may be the source of the mystery
why
bytes are signed). The only time the byteness comes into play is
when
you write code of the form:

b = ba[ i ];

then the sign bit is propagated.

OK, but println expects an int. Try this:

void callDisplay()
{
byte b1 = 254;
display(b1);
}

void display( byte b )
{
// It is irrelevant here whether b1 was sign-extended or not
// "b" will contain the same 0xFE bit pattern regardless, because
// the top 24 bits of the argument get masked off
}
 
R

Roedy Green

// It is irrelevant here whether b1 was sign-extended or not
// "b" will contain the same 0xFE bit pattern regardless, because
// the top 24 bits of the argument get masked off
}

I wrote a little program to prove my point:


import static java.lang.System.out;

public class StudyByte
{
static void display ( byte b )
{
out.println( b );
}

/**
* test harness
*
* @param args not used
*/
public static void main ( String[] args )
{
byte b = (byte) 254;
display ( b );
}
}

Here is how Javap disassembles it.

Compiled from "StudyByte.java"
public class StudyByte extends java.lang.Object
SourceFile: "StudyByte.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method #6.#17; // java/lang/Object."<init>":()V
const #2 = Field #18.#19; //
java/lang/System.out:Ljava/io/PrintStream;
const #3 = Method #20.#21; //
java/io/PrintStream.println:(I)V
const #4 = Method #5.#22; // StudyByte.display:(B)V
const #5 = class #23; // StudyByte
const #6 = class #24; // java/lang/Object
const #7 = Asciz <init>;
const #8 = Asciz ()V;
const #9 = Asciz Code;
const #10 = Asciz LineNumberTable;
const #11 = Asciz display;
const #12 = Asciz (B)V;
const #13 = Asciz main;
const #14 = Asciz ([Ljava/lang/String;)V;
const #15 = Asciz SourceFile;
const #16 = Asciz StudyByte.java;
const #17 = NameAndType #7:#8;// "<init>":()V
const #18 = class #25; // java/lang/System
const #19 = NameAndType #26:#27;// out:Ljava/io/PrintStream;
const #20 = class #28; // java/io/PrintStream
const #21 = NameAndType #29:#30;// println:(I)V
const #22 = NameAndType #11:#12;// display:(B)V
const #23 = Asciz StudyByte;
const #24 = Asciz java/lang/Object;
const #25 = Asciz java/lang/System;
const #26 = Asciz out;
const #27 = Asciz Ljava/io/PrintStream;;
const #28 = Asciz java/io/PrintStream;
const #29 = Asciz println;
const #30 = Asciz (I)V;

{
public StudyByte();
Signature: ()V
LineNumberTable:
line 3: 0



Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0


static void display(byte);
Signature: (B)V
LineNumberTable:
line 7: 0
line 8: 7



Code:
Stack=2, Locals=1, Args_size=1
0: getstatic #2; //Field
java/lang/System.out:Ljava/io/PrintStream;
3: iload_0
4: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
7: return
LineNumberTable:
line 7: 0
line 8: 7


public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
LineNumberTable:
line 17: 0
line 18: 3
line 19: 7



Code:
Stack=1, Locals=2, Args_size=1
0: bipush -2 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
2: istore_1
3: iload_1
4: invokestatic #4; //Method display:(B)V
7: return
LineNumberTable:
line 17: 0
line 18: 3
line 19: 7


}


The key is bipush -2. It is pushing a 32-bit sign extended -2 onto the
the stack, not 0xfe as you thought. bytes are treated like signed
32-bit ints inside the JVM.

--
Roedy Green Canadian Mind Products
http://mindprod.com

"Deer hunting would be fine sport, if only the deer had guns."
~ William S. Gilbert of Gilbert and Sullivan
 
A

Arne Vajhøj

Mike said:
Now, now, I agree that signed byte were a terrible botch, but let's
not consider a quick death sufficient punishment.

Agreed.

Something from a medieval torture chamber.

Arne
 
M

Mike Schilling

Roedy said:
I wrote a little program to prove my point:

You're right. I had thought bytes where sign-extended when necessary, when
apparently they're sign-extended all the time.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top