Tricky Java Number Stuff that makes your head spin
DeegeU Quick Hits
Tricky Java Number Stuff is part of a larger collection called “Quick Hits”. You can find more information about this in the “Quick Hits” section.
Transcript – Tricky Java Number Stuff
If you’re following along in the Free Java Online Course, Tricky Java number stuff is a good topic to hit after we’ve covered primitives and operators. One lesson I tell everyone when they are learning about computers is “Computers do what you tell them to, not what you want them to”. It’s especially true when programming. Too often you’ll say I know what will happen, and you’ll be surprised by the outcome. Once you think about it a bit more, you’ll realize your mistake and say “Oh yeah”. Here’s a few “Oh yeah” moments for you.
If you’re new to programming in Java, and this video shocks you, don’t worry. There are programmers out there who have many years of experience, and this video will still blow their mind! Let’s start!
Divide by zero
Every programmer, from the day they start to learn programming, you’re taught “don’t divide by zero”. It’s bad. Computers die. Don’t do it. Let’s do it. What happens when we divide 1 by 0?
public class Test { public static void main(String[] args) { System.out.println(1 / 0); } }
Boom. Java dies. ArithmeticException: / by zero. Oops. I guess that was expected.
Wanna do it again? Let’s try it with floating point numbers. What does this print?
public class Test { public static void main(String[] args) { System.out.println(1.0 / 0.0); } }
Huh. It prints infinity. No error.
So can you verify if an answer is infinity? If you tested the value against POSITIVE_INFINITY, you’d get… well what do you get?
What happens when you compare (1.0 / 0.0) == infinity? We run it, and you get true. If you guessed true, you guessed wisely.
What about 0.0 divided by 0.0? What would that print? Well that doesn’t make sense.
And Java agrees. Java returns NaN. Now what happens if we compare 0.0 / 0.0 and 0.0 / 0.0? Are these equal?
Nope. You can’t compare NaNs. That always returns false when you use the equality operator. If you want to check for NaNs, you need to use the float method isNaN. Let’s play with floating point numbers more.
Floating point min values
Let’s see if you know what min values are. What happens in this code. What gets printed? You first thought might be, well this is a trick question, so it has to be the min value. Or is the trick that it’s the zero.
public class Test { public static void main(String[] args) { System.out.println(Math.min(Float.MIN_VALUE, 0.0f)); } }
Many programmers outsmart themselves when they see this question. Negative numbers are smaller than zero. So this code prints -2.147483648E9. Not all that surprising, but some people are tripped up on it. So let’s double down with doubles. What does this print?
public class Test { public static void main(String[] args) { System.out.println(Math.min(Double.MIN_VALUE, 0.0d)); } }
If you’re not sure what I changed, I’m now using the Double class for the min value.
Well we already saw that, this is just with doubles. So we’re going to double down and say it’s some bigger negative number, right? BUUUUZZZZZ!
It prints 0. MIN_VALUE for doubles is the smallest positive minimum value you can represent in a double. The smallest, that is, without actually being zero. The minimum value for a float is negative, and the minimum value for a double is postive. If you guessed 0, you get a gold star. The rest of you…. mind blown.
Byte literals
Now for a bit of bytes. We know all numbers in Java are 2’s complement numbers. We can even store numbers into a byte as a bit literal like this. If we wanted to store 1, we can define our byte like this.
byte b = 0b00000001;
How do we define -1 using bits? Well since it’s a two’s complement number, let’s try that. -1 as bits is all ones.
byte b = 0b11111111;
And that won’t even compile. It’s saying the bits are an integer. How do you declare -1 as a byte? Add a minus sign to the binary 1. That’s it. Literals are literals, not 2’s complement numbers.
Alright, if I gave you a byte, call it b. Are these three statements equivalent? That is, do these three statements do the same thing?
byte b = 0; b++; b+=1; b = b + 1;
Nope. The first two will insert a cast back to byte when you compile. In the first two cases, b will evaluate to 2 after execution. And the third? Any guesses? The third won’t compile since 1 is an integer literal. The 1 causes a widening conversion, and the addition returns an integer, not a byte.
Well what happens when you try to add two bytes? Neither are an integer, like the literal. This won’t even compile. Addition returns integers. Doesn’t matter if it’s a byte, or even if your answer fits into a byte.
Well what happens when you use a unary operator, and add something that clearly will not
fit into a byte. Let’s do it!
b+=134;
Will it compile? Will it run? Yes it will compile, and yes it will run. It will print -122. You can even add something that clearly is not a byte, like 50,000. That result is 80. The result you’re getting is the number that is modulo congruent to the number you’re trying to calculate. If that surprises you, you might want to take a look at the How do computers store numbers lessons on DeegeU.com.
Integer class equality and autoboxing
OK, last one for now. Let’s play with the Integer class and autoboxing.
If we have an Integer, and set it to 1. Then create another integer and set it to 1. Are the two integers equal using the equality operator? Remember, the Integers are classes and the equality operator will test to see if the two variables reference the same integer class.
Integer a = 1; Integer b = 1; if (a == b) { System.out.println(“a and b are equal”); } else { System.out.println(“a and b are NOT equal”); }
And they are equal. Did that surprise you? Ok, lets do the same exact thing, but in this case let’s use 128.
And they are not equal. These do not point to the same Integer instance. If you use 1 and 1, they’re equal. If you use 128 and 128, they are not equal. Mind blown.
The reason is Java doesn’t like creating objects if it doesn’t need to. For numbers less than 127, Java creates a cache of Integer instances, anything within the range of a byte. So that’s -128 to 127. You can verify this in the valueOf method of the Integer class.
So we’ve seen that we can divide by zero when using floating point numbers, we cannot compare NaNs, integers and doubles have minimum values that differ in sign, literals are literals, it sucks doing math with bytes, and number classes are not always created when you create one.
Know any other crazy number behavior in core Java? Let me know in the comments below!
Till next time!
Tools Used
- Java
- NetBeans
Media Credits
All media created and owned by DJ Spiess unless listed below.
- No infringement intended
Get the code
The source code for “Tricky Java Number Stuff” can be found on Github. If you have Git installed on your system, you can clone the repository by issuing the following command:
git clone https://github.com/deege/deegeu-tricky-number-stuff.git
Go to the Support > Getting the Code page for more help.
If you find any errors in the code, feel free to let me know or issue a pull request in Git.
Don’t miss another video!
New videos come out every week. Make sure you subscribe!
Comments
DJ Spiess
Your personal instructor
My name is DJ Spiess and I’m a developer with a Masters degree in Computer Science working in Colorado, USA. I primarily work with Java server applications. I started programming as a kid in the 1980s, and I’ve programmed professionally since 1996. My main focus are REST APIs, large-scale data, and mobile development. The last six years I’ve worked on large National Science Foundation projects. You can read more about my development experience on my LinkedIn account.