Understanding a program written into bluej using twos complement. - java

I just started using Bluej to learn more about how computers store integers. I have a small program that I put into Bluej that sets the value of an integer called x to MAX_VALUE - 3 then adds 1 to x six times, printing out a new value each time.
One addition is incorrect, although I need help understanding which value I received in incorrect and why the results I got are "strange".
Please keep in mind I am VERY naive to the language for computers and am literally reading from a book about storing integers. The book I have is Computer Science 11th edition by J. Glenn Brookshear.
Here is the program I put into BlueJ:
public class Add
{
public Add()
{
int i, x;
x = java.lang.Integer.MAX_VALUE - 3;
i = 0;
while (i < 6) {
x = x + 1;
i = i + 1;
System.out.print(x + "\n");
}
}
}
The values I receive are:
2147483645
2147483646
2147483647
-2147483648
-2147483647
-2147483646
My teacher says there is a problem with any integer math but I do not know what he means. I would just really like to understand why this happens.
I might also note that these numbers are very much larger than 1 and I do not know why.
Thank you all in advance for any responses!

Integers that you store with the int data type are only allocated a limited amount of space in your computer's memory. It's not possible to store every possible integer in this amount of space. So your computer will deal correctly with integers between -2147483648 and 2147483647, because those are enough for most purposes. If you want to store numbers that are outside this range, you need to use a different data type from int. For example, there's long (which has a much bigger range) and BigInteger (which is really limited only by the amount of space allocated to Java itself).
When you add 1 to the largest possible int, the "correct" answer can't fit in an int variable. This is a bit like having an abacus with only one line of beads (which can represent numbers from 0 to 9), and trying to work out 9 + 1. Your computer will roll the number over to the smallest possible int instead. So when you work with int values, the effect is that 2147483647 + 1 = -2147483648, even though mathematically this makes no sense.

There is a limit value for an integer in Java in this case max_value.... for example when you try to surpass that value it becomes the oposite (-2,147,483,648 min_value). Like completing the circle and go back to the beggining. So there in no higher value than 2,147,483,647 ...so when you add 1 to that value you get the min_value instead...think of it like a snake eating his own tail ;)

If your Windows calculator has a Programmer View, switch to it, click Dword, enter 2147483645, add 1 six times, and watch the bits.
An integer in Java is 32-bits and signed. This means there is one sign bit and 31 bits for the value.
Integer.MAX_VALUE = 2147483647 (base 10)
= 0111 1111 1111 1111 1111 1111 1111 1111 (base 2)
Adding 1 yields
2147483647 + 1 = 2147483648
= 1000 0000 0000 0000 0000 0000 0000 0000
Counting up, this is what you'd expect if you weren't a computer (or your number wasn't bounded by representation space). But with the int data type, we only get 32 bits and the "first" (not technically correct, but will aid in understanding) tells you whether or not the value is negative.
Now when Java translates this value to base 10 and because this is the signed integer data type...
2147483647 + 1 = 2147483648
= 1000 0000 0000 0000 0000 0000 0000 0000
We read the first bit as 1 so this is a negative number and we need to take its
twos-complement to calculate the value.
= 1000 0000 0000 0000 0000 0000 0000 0000
negated = 0111 1111 1111 1111 1111 1111 1111 1111
+ 1 = 1000 0000 0000 0000 0000 0000 0000 0000
= 2147483648 (base 10)
so when we display this value, it's the negative value of the two's complement,
= -2147483648
The problem with "integer math" your teacher mentions is that, when your data type (Java's int, in this case) is bounded by size, your operations must make sense within its range.

Related

how does a number change when it is too long for the selected datatype in java [duplicate]

This question already has answers here:
How are integers cast to bytes in Java?
(8 answers)
Type casting into byte in Java
(6 answers)
Explicit conversion from int to byte in Java
(4 answers)
Closed 4 months ago.
For example of this is my input:
byte x=(byte) 200;
This will be the output:
-56
if this is my input:
short x=(short) 250000;
This will be the output:
-12144
I realize that the output is off because the number does not fit into the datatype, but how can I predict what this output will be in this case? In my computer science exam this my be one of the questions and I do not understand why exactly 200 changes to -56 and so one.
I realize that the output is off because the number does not fit into the datatype, but how can I predict what this output will be in this case? In my computer science exam this my be one of the questions and I do not understand why exactly 200 changes to -56 and so one.
The relevant aspects are what overflow looks like, and how the bits that represent the underlying data are treated.
Computers are all bits, grouped together in groups of 8; a group of 8 bits is called a byte.
byte b = 5; for example, is stored in memory as 0000 0101.
Bits can be 0. Or 1. That's it. That's where it ends. And everything is, in the end, bits. This means: That - is not a thing. Computers do not know what - is and cannot store it. We need to write code and agree on some sort of meaning to represent them.
2's complement
So what's -5 in bits? It's 1111 1011. Which seems bizarre. But it's how it works. If you write: byte b = -5;, then b will contain 1111 1011. It is because javac made that happen. Similarly, if you then call System.out.println(b), then the println method gets the bit sequence 1111 1011. Why does the println method decide to print a - symbol and then a 5 symbol? Because it's programmed that way: We all are in agreement that 1111 1011 is -5. So why is that?
Because of a really cool property - signed/unsigned irrelevancy.
The rule is 2's complement: To switch the sign (i.e. turn 5, which is 0000 0101 into -5 which is 1111 1011), you flip every bit, and then add 1 to the end result. Try it with 0000 0101 - and you'll see it's 1111 1011. This algorithm is reversible - apply the same algorithm (flip every bit, then add 1) and you can turn -5 into 5.
This 2's complement thing has 2 great advantages:
There is only one 0 value. If we just flipped all bits, we'd have both 1111 1111 and 0000 0000 both representing some form of 0. In basic math, there's no such thing as 'negative 0' - it's the same as positive 0. Similarly if we just decided the first bit is the sign and the remaining 7 bits are the number, then we'd have both 1000 0000 and 0000 0000 both being 0, which is annoying and inefficient, why waste 2 different bit sequences on the same number?
plus and minus are sign-mode independent. The computer doesn't have to KNOW whether we are doing the 2's complement thing or not. Take the bit sequence 1111 1011. If we treat that as unsigned bits, then that is 251 (it's 128 + 64 + 32 + 16 + 8 + 2 + 1). If we treat that as a signed number, then the first bit is 1, so the thing is negative: We apply 2's complement and figure out that it is -5. So, is it -5 or 251? It's both, at once! Depends on the human/code that interprets this bit sequence which one it is. So how could the computer possibly do a + b given this? The weird answer is: It doesn't matter - because the math works out the same way. 251 - 10 is 241. -5 - 10 is -15. -15 and 241 are the exact same bit sequence.
Overflow
A byte is 8 bits, and there are 256 different sequences of bits, and then you have listed each and every possible variant. (2^8 = 256. Hence, a 16-bit number can be used to convey 65536 different things, because 2^16 is 65536, and so on). So, given that bytes are 8 bits and we decreed they are signed, and 2's complement signed, that means that the smallest number you can send with it is -128, which in bits is 1000 0000 (use 2's complement to check my work), and +127, which in bits is 0111 1111. So what happens if you add 1 to 127? That'd seemingly be +128 except that's not storable in 8 bits if we decree that we interpret these bits as 2's complement signed (which java does). What happens? The bits 'roll over'. We just add 1 as normal, which turns 0111 1111 into 1000 0000 which is -128:
byte b = 127;
b = (byte)(b + 1);
System.out.println(b); // prints -128
Imagine the number line - stretching out into infinity on both ends, from -infinite to +infinite. That's the usual way math works. Computers (or rather, int, long, etc) do not work like that. Instead of a line, it is a circle. Take your infinite number line and take some scissors, and snip that number line at -128 (because a 2's comp signed byte cannot represent -129 or anything else below -128), and at +127 (because our byte cannot represent 128 or anything above it).
And now tape the 2 cut ends together.
That's the number line. What's 'to the right' of 125? 126 - that's what +1 means: Move one to the right on the number line.
What's 'to the right' of +127? Why, -128. Because we taped it together.
Similarly, -127 - 5 is +123. '-5' is 'move 5 places to the left on the number line (or rather, number circle)'. Going in 1 decrements:
-127 (we start here)
-128 (-127 -1)
+127 (-127 -2)
+126 (-127 -3)
+125 (-127 -4)
+124 (-127 -5)
Hence, 124.
Same math applies to short (-32768 to +32767), char (which is really a 16-bit unsigned number - so 0 to 65535), int (-2147483648 to +2147483647), and even long (-2^63 to +2^63-1 - those get a little large).
short x = 32765;
x += 5;
System.out.println(x); // prints -32766.

Why 2^31 can't store in integer variable but - 2^31 can?

Integer variables are 4-bytes or 32-bits, and 2^31 and -2^31 both in binary numbers are 32 bits. But when you put 2^31 = 2,147,483,648 in an integer variable it shows an error, but for -2^31 it is ok. Why?
Integer variables are 4-bytes or 32-bits, and 2^31 and -2^31 both in binary numbers are 32 bits
No they are not.
in basic binary, negative numbers aren't a thing. We have zeroes and ones. There is no - sign.
In binary, 2^31 becomes:
1000 0000 0000 0000 0000 0000 0000 0000
In binary, -2^31 cannot be represented without first defining how negative numbers are to be stored.
Commonly (and java does this too), a system called 2's complement is used. 2's complement sounds real complicated: Take the number, say, 5. Represent it in binary (for this exercise, let's go with byte, i.e. 8 bits): 0000 0101. Now, flip all bits: 1111 1010, and then add 1: 1111 1011.
That is -5 in signed 2's complement binary.
This bizarre system has two amazing properties: Math continues to work as normal without needing to know if the number is signed or unsigned. Let's try it. -5 + 2 is -3, right? let's see.. what's 1111 1011 + 0000 0010? Without worrying about 2's complement at all, I get 1111 1101. Let's apply 2's complement conversion: first flip the bits: 0000 0010, then add 1: 0000 0011, which is... 3. So -5 + 2 is -3. Check. The other amazing property is that it doesn't 'waste' 2 of the 2^32 "slots" on zeroes. Let's try the 2's complement of 0: 0000 0000, then flip all bits: 1111 1111, then add 1: 0000 0000 (with a bit overflow that we ignore). That's nice: 0 is its own 2's complement. We can't tell 0 and -0 apart, but that's generally a good thing.
Another property of this system is that the first bit is the 'sign' bit. if it is 1, it is negative, if 0, it is not.
Let's try to 2's complement 1000 0000 0000 0000 0000 0000 0000 0000. First, flip the bits: 0111 1111 1111 1111 1111 1111 1111 1111. Then add 1: 1000 0000 0000 0000 0000 0000 0000 0000. Wait. That's... what we had!!
Yup. and because the first bit is negative, 1000 0000 0000 0000 0000 0000 0000 0000 is NEGATIVE.
Perhaps you are forgetting that 0 is a thing, and 0 is neither positive nor negative.
So, if 0 needs to be representable, and gets a 0 sign bit (zero in bits is 0000000... of course), that means the 'space' in the half of all representable numbers that start with a 0 is now one smaller, because 0 has eaten one slot. That means there is one more negative number representable vs. the positive numbers. (or, alternatively, that 0 'counts' as positive, therefore 0 is the first positive number, but -1 is the first negative number). Therefore, there must be at least 1 negative number that has no positive equivalent in 2's complement. That number is... 2^31. -2^31 fits in 32-bit signed. +2^31 doesn't.
Let's imagine a 3-bit signed number, with 2's complement. We can list them all:
000 = 0
001 = 1
010 = 2
011 = 3
100 = -4
101 = -3
110 = -2
111 = -1
Note how -4 is in there, but +4 is not, and note how we covered 8 numbers. 2^3 = 8 - 3 bits can represent 8 numbers, not more than that.
From the oracle documentation we got that:
int: By default, the int data type is a 32-bit signed two's complement integer, which has a minimum value of -2^31 and a maximum value of 2^31-1. In Java SE 8 and later, you can use the int data type to represent an unsigned 32-bit integer, which has a minimum value of 0 and a maximum value of 2^32-1. Use the Integer class to use int data type as an unsigned integer. See the section The Number Classes for more information. Static methods like compareUnsigned, divideUnsigned etc have been added to the Integer class to support the arithmetic operations for unsigned integers.
From another document(simple and quite understandable) we got:
When an integer is signed, one of its bits becomes the sign bit, meaning that the maximum magnitude of the number is halved. (So an unsigned 32-bit int can store up to 2^32-1, whereas its signed counterpart has a maximum positive value of 2^31-1.)
In Java, all integer types are signed (except char).
Is because the first bit indicate the sign bit. Maximum positive value it can store it 2^31 - 1. There are many resources available for this.

Java and unsigned values

I'm parsing unsigned bits from a DatagramSocket. I have a total of 24bits (or 3 bytes) coming in - they are: 1 unsigned 8bit integer followed by a 16bit signed integer. But java never stores anything more than a signed byte into a byte/byte array? When java takes in these values, do you lose that last 8th bit?
DatagramSocket serverSocket = new DatagramSocket(666);
byte[] receiveData = new byte[3]; <--Now at this moment I lost my 8th bit
System.out.println("Binary Server Listing on Port: "+port);
while (true)
{
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
byte[] bArray = receivePacket.getData();
byte b = bArray[0];
}
Did I now lose this 8th bit since I turned it into a byte? Was it wrong I initialized a byte array of 3 bytes?
When java takes in these values, do you lose that last 8th bit?
No. You just end up with a negative value when it's set.
So to get a value between 0 and 255, it's simplest to use something like this:
int b = bArray[0] & 0xff;
First the byte is promoted to an int, which will sign extend it, leading to 25 leading 1 bits if the high bit is 1 in the original value. The & 0xff then gets rid of the first 24 bits again :)
No, you do not lose the 8th bit. But unfortunately, Java has two "features" which make it harder than reasonable to deal with such values:
all of its primitive types are signed;
when "unwrapping" a primitive type to another primitive type with a greater size (for instance, reading a byte to an int as is the case here), the sign bit of the "lower type" is expanded.
Which means that, for instance, if you read byte 0x80, which translates in binary as:
1000 0000
when you read it as an integer, you get:
1111 1111 1111 1111 1111 1111 1000 0000
^
This freaking bit gets expanded!
whereas you really wanted:
0000 0000 0000 0000 0000 0000 1000 0000
ie, integer value 128. You therefore MUST mask it:
int b = array[0] & 0xff;
1111 1111 1111 1111 1111 1111 1000 0000 <-- byte read as an int, your original value of b
0000 0000 0000 0000 0000 0000 1111 1111 <-- mask (0xff)
--------------------------------------- <-- anded, give
0000 0000 0000 0000 0000 0000 1000 0000 <-- expected result
Sad, but true.
More generally: if you wish to manipulate a lot of byte-oriented data, I suggest you have a look at ByteBuffer, it can help a lot. But unfortunately, this won't save you from bitmask manipulations, it is just that it makes it easier to read a given quantity of bytes as a time (as primitive types).
In Java, byte (as well as short, int and long) is only a signed numeric data types. However, this does not imply any loss of data when treating them as unsigned binary data. As your illustration shows, 10000000 is -128 as a signed decimal number. If you are dealing with binary data, just treat it as its binary form and you will be fine.

Using a larger prime as a multiplier when overriding hashCode()

I have been reading about hashcode functions for the past couple of hours and have accumulated a couple of questions regarding use of prime numbers as multipliers in custom hashcode implementations. I would be grateful if I could get some insight regarding following questions:
In a comment to #mattb's answer here, #hstoerr advocates for use of larger primes (such as 524287) instead of the common prime 31. My question is, given the following implementation of a hashcode functions for a pair or elements:
#Override
public int hashCode() {
final int prime = 31;
int hash1 = (pg1 == null) ? 0 : pg1.hashCode();
int hash2 = (pg2 == null) ? 0 : pg2.hashCode();
return prime * (hash1 ^ hash2);
}
doesn't this lead to an overflow on the returned int if prime is a large number?
Assuming that the overflow is not a problem (JVM doing an automatic cast) is it better to do a bitshift instead of a cast?
I imagine the performance of the hashcode function vary significantly based on the complexity of the hashcode. Does the size of the prime multiplier not effect the performance?
Is it better/smarter/faster to use multiple primes in a custom hashcode function instead of a single multiplier? If not, is there some other advantage? See the example below from #jinguy's answer to a relevant question:
public int hashCode() {
return a * 13 + b.hashCode() * 23 + (c? 31: 7);
}
where a is an int, b is a String and c is boolean.
How about something like long lhash = prime * (hash1 ^ hash2); then using (int)((lhash >> 32) ^ lhash)? That's something I saw on another question here SO, but it wasn't really explained why it was a good idea to do it like that.
Apologies in advance for the novel. Feel free to make suggestions or edit directly. --Chet
There is an overflow, but not an exception.
The danger doesn't come from losing accuracy, but losing range. Let's use a ridiculous example, where "prime" is a large power of 2, and 8-bit unsigned numbers for brevity. And assume that (hash1 ^ hash2) is 255:
"prime": 1000 0000
(hash1 ^ hash2): 1111 1111
Showing the truncated digits in brackets, our result is:
product: [0111 1111] 1000 0000
But multiplying by 128 is the same as shifting left by 7 places. So we know that whatever the value of (hash1 ^ hash2), the least-significant places of the product will have seven zeros. So if (hash1 ^ hash2) is odd (least significant bit = 1), then the result of multiplying by 128 will always be 128 (after truncating the higher digits). And if (hash1 ^ hash2) is even (LSB is 0, then the product will always be zero.
This extends to larger bit sizes. The general point is that if the lower bits of "prime" are zeros, you're doing a shift (or multiple shift + sum) operation that will give you zeros in the lower bits. And the range of the product of multiplication will suffer.
But let's try making "prime" odd, so that the least significant bit will always be 1. Think about decomposing this into shift / add operations. The unshifted value of (hash1 ^ hash2) will always be one of the summands. The least significant bits that were shifted into guaranteed uselessness by an even "prime" multiplier will now be set based on, at minimum, the bits from the original (hash1 ^ hash2) value.
Now, let's consider a value of prime which is actually prime. If it's more than 2, then we know it's odd. So the lower bits haven't been shifted into uselessness. And by choosing a sufficiently large prime, you get better distribution across the range of output values than you'd get with a smaller prime.
Try some exercises with 16-bit multiplication using 8443 (0010 0000 1111 1011) and 59 (0000 0000 0011 1011). They're both prime, and the lower bits of 59 match the lower bits of 65531. For example, if hash1 and hash2 are both ASCII character values (0 .. 255), then all of the results of (hash1 ^ hash2) * 59 will be <= 15045. This means that roughly 1/4 of the range of hash values (0..65535) for a 16-bit number go unused.
But (hash1 ^ hash2) * 8443 is all over the map. It overflows if (hash1 ^ hash2) is as low as 8. It uses all 16 bits even for very small input numbers. There's much less clustering of hash values across the overall range, even if the input numbers are in a relatively small range.
Assuming that the overflow is not a problem (JVM doing an automatic cast) is it better to do a bitshift instead of a cast?
Most likely not. The JVM should translate into an efficient implementation on the host processor anyway. Integer multiplication should be implemented in hardware. And if not, the JVM is responsible for translating the operation into something reasonable for the CPU. It's very likely that the case of integer multiplication is highly optimized already. If integer multiplication is done more quickly on a given CPU as shift-and-add, the JVM should implement it that way. But it's less likely that the folks writing the JVM would care to watch for cases where multiple shift-and-add operations could have been combined into a single integer multiply.
I imagine the performance of the hashcode function vary significantly based on the complexity of the hashcode. Does the size
of the prime multiplier not effect the performance?
No. The operations are the same when done in hardware regardless of the size, number of bits set, etc. It's probably a couple of clock cycles. It would vary depending on the specific CPU, but should be a constant-time operation regardless of the input values.
Is it better/smarter/faster to use multiple primes in a custom hashcode function instead of a single multiplier? If not, is there
some other advantage?
Only if it reduces the possibility of collisions, and this depends on the numbers you're using. If your hash code depends on A and B and they're in the same range, you might consider using different primes or shifting one of the input values to reduce overlap between the bits. Since you're depending on their individual hash codes, and not their values directly, it's reasonable to assume that their hash codes provide good distribution, etc.
One factor that comes to mind whether you want the hash code for (x, y) to be different from (y, x). If your hash function treats A and B in the same way, then hash(x, y) = hash(y, x). If that's what you want, then by all means use the same multiplier. It not, using a different multiplier would make sense.
How about something like long lhash = prime * (hash1 ^ hash2); then using (int)((lhash >> 32) ^ lhash)? That's something I saw on another question here SO, but it wasn't really explained why it was a good idea to do it like that.
Interesting question. In Java, longs are 64-bit and ints are 32-bit. So this generates a hash using twice as many bits as desired, and then derives the result from the high and low bits combined.
If multiplying a number n by a prime p, and the lowermost k bits of n are all zeros, then the lowermost k bits of the product n * p will also be all zeros. This is fairly easy to see -- if you're multiplying, say, n = 0011 0000 and p = 0011 1011, then the product can be expressed as the sum of two shift operations. Or,
00110000 * p = 00100000 * p + 00010000 * p
= p << 5 + p << 4
Taking p = 59 and using unsigned 8-bit ints and 16-bit longs, here are some examples.
64: 0011 1011 * 0100 0000 = [ 0000 1110 ] 1100 0000 (192)
128: 0011 1011 * 1000 0000 = [ 0001 1101 ] 1000 0000 (128)
192: 0011 1011 * 1100 0000 = [ 0010 1100 ] 0100 0000 (64)
By just dropping the high bits of the result, the range of the resulting hash value is limited when the low bits of the non-prime multiplicand are all zeros. Whether that's an issue in a specific context is, well, context-specific. But for a general hash function it's a good idea to avoid limiting the range of output values even when there are patterns in the input numbers. And in security applications, it's even more critical to avoid anything that would let someone make inferences about the original value based on patterns in the output. Just taking the low bits reveals the exact values of some of the original bits. If we make the assumption that the operation involved multiplying an input number with a large prime, then we know that the original number had as many zeros at the right as the hash output (because the prime's rightmost bit was 1).
By XORing the high bits with the low bits, there's less consistency in the output. And more importantly, it's much harder to make guesses about the input values based on this information. Based on how XOR works, it could mean the original low bit was 0 and the high bit was 1, or the original low bit was 1 and the high bit was 0.
64: 0011 1011 * 0100 0000 = 0000 1110 1100 0000 => 1100 1110 (206)
128: 0011 1011 * 1000 0000 = 0001 1101 1000 0000 => 1001 1101 (157)
192: 0011 1011 * 1100 0000 = 0010 1100 0100 0000 => 0110 1100 (204)
Overflow is not a problem. Hashes are constrained to a narrow value set anyway.
The first hash function you posted isn't very good. Doing return (prime * hash1) ^ hash2;
` instead would reduce the number of collisions in most cases.
Multiplying by a single word int is generally very fast, and the difference between multiplying by different numbers is negligible. Plus the execution time is dwarfed by everything else in the function anyay
Using different prime multipliers for each part may reduce the risk of collisions.

Why is Java able to store 0xff000000 as an int?

An integer's max value in Java is 2147483647, since Java integers are signed, right?
0xff000000 has a numeric value of 4278190080.
Yet I see Java code like this:
int ALPHA_MASK = 0xff000000;
Can anyone enlighten me please?
Just an addition to erickson's answer:
As he said, signed integers are stored as two's complements to their respective positive value on most computer architectures.
That is, the whole 2^32 possible values are split up into two sets: one for positive values starting with a 0-bit and one for negative values starting with a 1.
Now, imagine that we're limited to 3-bit numbers. Let's arrange them in a funny way that'll make sense in a second:
000
111 001
110 010
101 011
100
You see that all numbers on the left-hand side start with a 1-bit whereas on the right-hand side they start with a 0. By our earlier decision to declare the former as negative and the latter as positive, we see that 001, 010 and 011 are the only possible positive numbers whereas 111, 110 and 101 are their respective negative counterparts.
Now what do we do with the two numbers that are at the top and the bottom, respectively? 000 should be zero, obviously, and 100 will be the lowest negative number of all which doesn't have a positive counterpart. To summarize:
000 (0)
111 001 (-1 / 1)
110 010 (-2 / 2)
101 011 (-3 / 3)
100 (-4)
You might notice that you can get the bit pattern of -1 (111) by negating 1 (001) and adding 1 (001) to it:
001 (= 1) -> 110 + 001 -> 111 (= -1)
Coming back to your question:
0xff000000 = 1111 1111 0000 0000 0000 0000 0000 0000
We don't have to add further zeros in front of it as we already reached the maximum of 32 bits.
Also, it's obviously a negative number (as it's starting with a 1-bit), so we're now going to calculate its absolute value / positive counterpart:
This means, we'll take the two's complement of
1111 1111 0000 0000 0000 0000 0000 0000
which is
0000 0000 1111 1111 1111 1111 1111 1111
Then we add
0000 0000 0000 0000 0000 0000 0000 0001
and obtain
0000 0001 0000 0000 0000 0000 0000 0000 = 16777216
Therefore, 0xff000000 = -16777216.
The high bit is a sign bit. Setting it denotes a negative number: -16777216.
Java, like most languages, stores signed numbers in 2's complement form. In this case, subtracting 231, or 2147483648 from 0x7F000000, or 2130706432, yields -16777216.
Something probably worth pointing out - this code is not meant to be used as an integer with a numerical value; The purpose is as a bitmask to filter the alpha channel out of a 32 bit color value. This variable really shouldn't even be thought of as a number, just as a binary mask with the high 8 bits turned on.
the extra bit is for the sign
Java ints are twos complement
ints are signed in Java.

Categories

Resources