So what happens when we need to represent numbers and fractions?
So far we've covered integer types only. For real numbers
we have two data types for fractional numbers, and again in different sizes.
The two data types are float and double. Float is short for floating-point
and double just means double size floating-point. These primitives are used
to represent
real numbers in Java. Our
goals for this lesson are to learn about floating point numbers,
also known as real numbers, fractional numbers, decimal numbers
or numbers from a continuous number line. You'll also learn about how floating
point numbers are stored
differently than the integers. You'll learn that computers are great for whole numbers,
but they can really suck when you try and add decimal numbers.
So were finally gonna talk about when floating point numbers go bad.
Before we dive into the floating-point numbers
let's review. We can represent numbers as binary numbers.
They follow the same pattern as decimal numbers. Instead of being to the power of ten,
it's to the power of two. So instead of 1, 10, 100, 1000...
etc., each bit is 1, 2, 4, 8, 16, 32...
and so on. We covered this
in the lesson, "How do computers store numbers?". Binary numbers work the same way
for fractions.
just as 0.1 is really
one over ten, the first fractional bit for a binary number
is one over two. Then it's one over four,
one over eight, and so on. Which are just powers of two.
So if we took
1.75 decimal, in binary
it would be 1.11. Whole one is
two to the power 0, the first fractional digits is one over two,
and the second is one over four. One over two plus one over four
is 3 over 4 or 0.75, plus
two to the 0 power for our 1 equals
1.75. If you remember scientific notation,
that works too. Take the decimal number
101.3. That's 1.013
times ten to the two. So
if we had a binary number 101.11
That's represented as 1.0111
times 2 to the second power.
You're probably asking what does this all mean for floating-point numbers?
The float can represent numbers from... well
this one to that one. [see above]
The numbers can be positive or negative. The bits are ordered a different way than
they were for integers.
It's got three parts, the sign, the exponent,
and the mantissa. The first bit says
if a number is positive or negative. Thats the sign bit.
The mantissa is our number in 23 bits,
normalized as a scientific notation number.
The exponent is eight bits, and it's like the exponent in our binary scientific notation,
but the exponent is biased. The word
biased is an engineering thing. What it means in this case is
we're shifting the value by 127.
So if the value in the exponent is three,
what it really means is 127 plus three,
or 130. So why would you want to do that?
Well we want negative exponents too, and this way we can represent
fractional numbers that have no whole part.
There's a couple of other numbers you can represent.
If everything is 0, then the number is 0.
The sign bit doesn't really matter. If the sign bit is set,
and everything else is 0, that is still 0. Sometimes
that's called negative 0. If all the exponent bits are set, but the mantissa bits are
all zero,
that's a special number. That's infinity.
The sign bit tells us if it's positive or negative infinity.
If all the exponent bits are set
and at least one mantissa bit is set, that's another special number
called NaN. It stands for
not a number. It's useful for when you divide by zero or take the square root
of a negative number.
It doesn't matter which mantissa bit or how many are set.
Now you can set you're floating point number to and number, zero,
infinity, or not a number. So let's represent 16.125 using a float.
Float is a 32-bit number. We can represent
the 16 with 10000.
The fractional part is .001 That makes the binary number
10000.001
To represent that in our scientific notation
we have 1.0000001
binary times
two to the fourth.
So now we have to place this all in our floating-point bits. The sign is positive
so that zero. 0 goes into the 32 bit.
The exponent is four. Remember we need to bias this number so
that makes our exponent 127 plus four.
Or 131. We store 131
into bits 24 to 31, And that's this number.
The last bit is the mantissa.
Our scientific binary number is 1.0000001
We know the first digit is one, so
we don't bother storing that. We just store 000001
for the mantissa and the rest the digits are zero.
So there's our final number. And that
is what gets stored into a float. A double
is just a bigger float. It uses one bit for the sign,
11 bits for the exponent, and 53 bits for the mantissa.
It gives you more precision and bigger values. So those are you paying attention
at home are thinking,
wait! 1 plus 11 plus 53, that's 65.
This is a 64-bit number. What gives? Well
if you remember the first digit of the mantissa is always one.
And we never store it. It's an implied bit. That's why we use the scientific notation.
That way we know the first bit is always one and we get it for free.
Let's go create some floats and doubles.
To create the types in Java, we use the same pattern we did for integer types.
We give the type, either float or double, we give it a name
for our number, and then possibly value. When defining a float
you need to add the letter "f" at the end of your literal. That tells Java it's a
float.
If you leave it off, Java thinks your literal is a double. You can add
"d" to the end of your double literals for clarity, but the
default is just double.
Luckily, we don't have to do all that bit manipulation to create our numbers.
We can assign our float a decimal literal, either as a floating point
number
or as a number in scientific notation. You can use a lowercase
or uppercase "e". In this particular case, Java is case-insensitive.
We run the app, and it prints our numbers back out.
Let's go try creating that error where we assign a double to float.
We'll try creating a float, then assigning a number without the "f" the end.
We get that lossy conversion error again. That's ok though because, most the time
you're gonna be working with doubles. There's really little use
for using floats these days. You can also set the numbers to the special
numbers we talked about before.
Using the Float and Double number classes. Here's what we do to set it as a NaN.
We'll cover these classes later. We can set this to negative infinity,
And we can do the same for doubles. When we run
it, it prints out the string for the value. You probably noticed
if some of the digits are used to represent exponents, they can't be used
represent a particular individual number, you know like in integers.
This means the you have holes in your number line. Its
just not possible to represent all numbers. Some floating point numbers
just can't be represented in a float or a double. And if it can't be represented
it gets rounded to the nearest number that it can represent.
This problem is even worse when you consider that
some simple numbers can't be represented at all.
Take the number one over three or one-third.
When you try to represent this is a fraction it goes on forever.
This is what we call a "repeating decimal". You can have the same problem with
binary numbers.
So one over three doesn't work in decimal.
Take 1 over 10, or one tenth.
The decimal number is 0.1 Try this in binary.
Take 10 in binary and divide by one.
You'll find that you get a repeating pattern in binary and the number will
never end.
This is something to remember, especially when you trying to represent money.
You can't represent ten cents! That sounds crazy!
So let's see this in action. Pretend we're adding
10 cents with 20 cents. That should give us 30 cents.
We're going to format the print statement,
because the normal print statement is going to round the results.
We'll print out the 16 decimal places for the float,
and 17 for the double. Run the program
and we get thirty cents plus
some random digits way out there. The more you add,
the more that error will move to the right. You should never ever use a primitive
floating point number for money.
So you're probably wondering why use them at all if they are so
error prone? The floating-point primitive types are
fast. You're exchanging accuracy for speed.
Since floats and doubles fit into 32 and 64 bits,
they are optimized for the instructions on your computer. There are instructions on your
CPU to handle these data types in a few clock cycles.
You'll often see these types used for graphics and for games.
Most of the math routines in Java are also using doubles.
So, if you're not constrained for memory, always prefer the double.
You get a bit more accuracy and the performance penalty really doesn't show
in modern computers.
So that's the float and the double. There's a much better, but
slower, thing you can use if you want more accuracy called the BigDecimal.
We'll cover that later when we get to primitive classes. Up next however
we'll look at the boolean and char primitives. And that will wrap it up for primitives.
Hey! Thanks for watching the video. There's a quick quiz for this on DeegeU.com if
you'd like to gauge how much you've learned.
If you like the videos you are seeing, please let me know by liking the video and
hitting the subscribe button for the DeegeU channel on YouTube. I'd really appreciate
that. If you have concerns or questions please leave them in the comments below
or on DeegeU.com.
There's a poll on the front page of DeegeU.com
so you can let me know what topic is covered next.
Thanks for watching and see you in the next video.