Java Programming   Java Programming
 |Sofia Home | Content Gallery |
Home
Syllabus
Schedule
Lessons
Assignments
Resources

Lesson 3.1 Numbers

Java Numbers

As you saw in Lesson 1 and 2, when you "look under the hood" of a Java program, what you find is a community of cooperating objects. When you take a closer look at those cooperating objects, what you find are more objects; the Font objects and Label objects and Button objects that make up the attributes of the applets you've built so far. 

Here in the real world, all of us understand that the objects we deal with every day--our cars and homes and even our pets and neighbors--are all constructed of simpler, and amazingly similar, primitive components: chemical compounds, molecules, and atoms. The same is true in the computer programming world --no matter what the language, at some level, everything is just bits.

The Great Divide

While Java does have some very rudamentary facilities for dealing with bits--the most primitive elements in the computer cosmology--you'll probably never have to use them.

There is a second kind of Java creature, however, that you're much more likely to encounter. These are the creatures that live midway between the minimalist world of the bit and the comfortable world of objects. We call these creatures  primitives, types , or fundatmental types.

What Are Primitives?

Primitives are much like Java objects, because they have identity, state, and behavior. They differ from full-fledged objects in four important ways:
  1. Primitives can hold only a single value. Java objects, by contrast, usually have many attributes, all of which can take on different values.
  2. Primitive values are manipulated by operators, while Java objects respond to messages (or method invocation).
  3. The behavior of primitives is built-in. You cannot change the way that a number responds to the addition operator, for instance. With objects, on the other hand, if you don't like a behavior, you can change it by creating your own subclass and writing a new method.
Back to Top

Why Primitives?

Many "pure" object-oriented languages, such as Smalltalk, have no primitive types. In those languages, everything--not just Buttons and Labels, but also numbers and characters--is an object. This has the advantage of consistency; users don't have to think about how to do an operation because everything is an object.

On the other hand, having a separate "class" of primitive types for numbers and characters also has some advantages. The two most important advantages are:

  • Simplicity: Think about summing two numbers. For most of us, the thought of telling a number object to add itself to another number object and then go tell a third number object the result is not nearly as intuitive as writing sum = price + tax. For arithmetic, at least, primitive types, along with operators and operands, seems easier to understand.
  • Efficiency: Primitive types consume less memory than objects do, and operations on primitive types run faster than equivalent operations on objects using methods.
Java has four "families" of primitive types:
  1. integers
  2. floating-point numbers
  3. characters
  4. true/false values called boolean 

In this section, we'll look at the two families of numeric primitives. Before we do that, let's take a short look at literals and variables.

Back to Top

Variables and Literals

Do you remember how to create an object attribute? If you were creating a grading applet that had a Label attribute called instructor, you could do it like this:
 
Label instructor = new Label("Steve Gilbert");

In this case, the field instructor is a variable because it holds a specific value. The formal definition of a variables is:

"A named storage area that holds a value"
The value that the field instructor holds is a reference to a specific Label object--a Label object that contains the text "Steve Gilbert". We call this a variable because, anytime we like, we can store a new value in the field instructor, like this:
 
instructor = new Label("Shin Karasuda");

Defining Variables

All variables must be defined before you can use them. The syntax for defining variables looks like this:
 
[modifiers] type name[ = initializer];
< The syntax elements appearing in brakets [] are optional. The brackets are not part of the syntax. We'll cover the first optional element, modifiers, later in the course. <

Definition tells the compiler what kind of value the variable will store [Label], the name of the storage location [instructor], and, optionally, an initial value to store in the location [= new Label("Steve Gilbert")].

You can define variables in two places:

  • Outside of any method. In Java, these kinds of variables are officially called fields. However, in the real world, you'll more often see them called instance variables, which is a general object-oriented term. When a variable is defined outside of a method, it can be used inside any method in the class. A variable declared outside of a method is automatically given a value when it is created, even if you don't specify one.
  • Inside a method. These types of variables are called local variables, and they can only be used inside the method where they were created. Unlike fields, local variables are not given an initial value automatically. You must provide an initial value explictly, or the compiler will report an error when you attempt to access the variable.
Variables using primitive types are defined exactly like object variables, with one exception [which we will cover in the next section]. To change the instructor field to store a primitive type such as an integer, instead of a Label, you'd just change the intialization like this:
 
int instructor = <provide integer value here>;
Back to Top

Initialization, Objects, Primitives, Literals

Variables are simply “buckets” [or "bottles", or "boxes", if you like] that hold values. It's up to you to put a value in the variable. As you've seen, you do that with the assignment operator [=].

 

Technical note. When used in this context, the assignment operator should be called the initalization operator because you are writing a definition statement, not an assignment statement. 

Assignment is used to give a value to a variable that already exists, not to provide an initial value to a newly created variable.

When you initialize an object variable, you first use the new operator, in conjunction with a constructor, to create the necessary object. Then, using the assignment operator, a reference to that object is stored in the variable you created.

When you initialize a primitive variable, you don't use the new operator. Instead, you'll normally write literals or constant primitive values directly into your code. When you do this, the primitive literal value you write--not a reference to that primitive value--will be stored in your variable. You'll see examples of this as we look at each primitive family in turn.

Now that you know about declaration and initialization, let's meet the first of our primitive families: the integers.

Back to Top

Meet the Integers

Integers are whole numbers, including zero and the negative numbers. 27 is an integer, as is -345. 7.25 is not an integer because it is not a whole number.

In math, integers are infinite. Think of the biggest integer you can imagine. No matter how big you make it, you can always make it bigger by adding one more digit onto the end. 

In constrast, integer primitves--in the computer sense--are finite because each integer number must be stored in a fixed-size region of memory.

Computer-style integers come in two "flavors": signed integers, which store both positive and negative whole numbers, and unsigned integers, which can store only positive whole numbers. All of Java integers are signed integers.

Storage Specifics

Java's integer family has four members, the byte, the short, the int, and the long. Each of these primitive types uses a specific amount of memory and thus, has a specific range of values that can be represented.

Here are the specifics:

  • A byte uses, as you'd expect, a byte [8 bits] of memory and can hold numbers from -128 to +127.
  • A short uses two bytes [16 bits] of memory and can represent any number in the range -32,768 to + 32,767.
  • An int, the most common number type you'll use in Java, uses four bytes [32 bits] of memory and can hold any number in the range -2,147,483,648 to +2,147,483,647, or, roughly, plus-or-minus two billion.
  • The grandaddy of all integer types is the long which uses eight bytes [64 bits] of memory and can hold values between, roughly, plus-or-minus nine quintillion.
Back to Top

Writing Integers

To construct an integer variable, you just use one of these four types where you would have used a class name, like this:

byte myAge = <byte literal here>;
short milesToGo = <short literal>;
int populationUSA = <int literal>;
long populationWorld = <long literal>;

Because primitive values don't use constructors or the new operator like objects do, you need to know how to write literal integer values into your code if you want to initialize your new integer variables. Fortunately, this is not difficult; in fact, you've been doing it already when you specified a size for your Font objects.

To write an integer literal you simply write out the number, without commas or decimals, like this:
byte myAge = 49;
short milesToGo = 23455;
int populationUSA = 263456732 ;
long populationWorld = 5374678328 ;

Literal Storage

When standing by themselves or when used in a calculation, integer literals use the int type, unless the literal number is larger than the largest int, in which case the long type is used. If a number is very small, [for instance, the literal number 3 which could easily be stored in a byte], it is still stored in an int, not in a byte

When used to initialize variables smaller than an int, such as myAge or milesToGo, Java will store the int in a variable of the appropriate size, after first making sure it will fit. While Java always uses the int type for less-than- long-value literals, you can force it to represent a small literal number using long storage by appending an L to the value like this:

3L ;

You'll find this useful when you learn about overloaded methods because it allows you to call a specific method by varying the type of the argument you pass.

Back to Top

Integer Bases

When you write integer literals of any type, Java assumes you are using the decimal [base 10] number system. [These are still integers, however. Don't start thinking decimal point!] For those of you with a need to write literals in a base other than 10, you have two options:
  • Java will automatically interpret an integer literal that starts with zero as an octal [base 8] number.
  • Java interprets literal numbers that start with zero-x, [or zero-X], as hexadecimal [base 16] numbers. 
For those of you with a need to write your integer literals in some other base, base-five or base-twelve, for instance, you're out of luck.

Here are some examples:

intten= 10;
inteight= 010;
intsixteen= 0x10;

Which One Should You Use?

Since you have the option of using four different types of integers, which one should you use? The general rule-of-thumb is that you should use the smallest type that will hold the largest value you expect.

Unfortunately, this rule of thumb can lead to some surprising problems because:

  • Smaller sizes won't necessarily save memory. Calculations always use int or long, never byte or short. You'll only save memory if you have large collections [arrays] of bytes or shorts.
  • Using an int when you should have used a long can create overflow problems, and you will not be warned. This can occur when any part of a calculation using int values becomes greater than (approximately) 2 billion. When this overflow occurs, your calculation will be incorrect. When deciding which type to use, you have to consider all intermediate calculations as well as the final result. 
Back to Top

Floating-point Numbers

Java has a second family of numeric primitive types, the floating-point family. Floating-point numbers can hold fractional values in addition to whole numbers. 

How Floating-Point Numbers Are Stored

Floating-point numbers can store fractional values because they are stored in a different internal format than binary integers. Instead of storing one simple binary value, floating-point numbers are stored as two binary numbers--called an exponent and a manitissa. When the actual value is used, a mathematical operation is performed by your computer using the two binary numbers to compute the final amount.

Floating-point Limitations

Because of this scheme, floating point numbers are often only an approximation of the real number you are trying to represent. 

Most of us are familiar with this concept from fifth and sixth grade when we learned about fractions and decimal numbers. There we found that 1 divided by 3 was 1/3, and 1/3+1/3+1/3 was 1.  When decimal numbers came round the next year, things got a little more complex; 1 divided by 3 became .3333333 and there didn't seem to be anyway to get back to where we started by adding the result. The same thing is true with binary floating point numbers as is true with decimal floating-point numbers. The only difference is that there are more repeating fractions than there are with decimal numbers.  This type of problem is called a representational error. Floating-point numbers are also susceptible to other kinds of errors that can snare the unwary or the extra-weary. If you add a very small number and a very large number together, for instance, you will often exceed the number of digits that the floating-point number can accurately represent.

Back to Top

Varieties of Floating-point Numbers

Java has two different primitive types for storing floating-point numbers, the float and the double. As with the integer family, each of these two types uses a different amount of memory and can represent different ranges of numbers.

While the integer types differ only in their range, floating-point types differ in their precision as well. The precision of a floating point number is measured as its significant digits. Here is a large number that is easily within the range of the float or double types:

1234567898765432123456.78987654321
Type the number into the text field shown below, however, and you'll see that Java only retains a certain number of digits, and seems to lose the fractional portion altogether.

Floating-point Stats

Here are the stats for the floating-point family:
  • The double, [which is the default format for floating point literals], uses eight-bytes [64 bits] of storage.
    • Its range can represent positive or negative numbers as small as 4.94E-324, and positive or negative numbers as large as 1.79E08. 

    • [For those of you not conversant with exponential notation, that last is the number 1.79 with the decimal point moved to right by 308 places. That is a very big number.]
       
    • It has a precision of approximately 15 digits

    •  
  • The float uses half the storage as a double, at four bytes [32 bits].
    • The float has about 7 digits of precision.
    • Its range can represent very small positive or negative numbers from 1.4E-45, and very large positive or negative numbers up to 3.4E8.

As you can see, the double type has a range approximately ten-times that of the float, although its precision is just a little more than twice the float [hence the name double, which is short for double precision.]

Back to Top

Writing Floating-point Literals

Just as with integers, you create floating-point variables by specifying the type [float or double] and a name, optionally followed by an initializer. As with the integers, you have to learn how to write floating-point literals to initialize your floating-point variables.

You can write floating-point literals in two different ways: using decimal notation or using exponential [scientific] notation. When you use decimal notation, you must have a digit and a decimal-point, but you don't need to have digits on both sides of the decimal, as you do in some languages such as Pascal.  These are all valid variables, initialized using floating-point literals and written using decimal notation:

doublefirst= .234;
doublesecond= 7.5432;
floatthird= 0.25F;

About That F

When you write a floating-point literal, Java automatically uses the double type. That creates a problem when you want to store the number in a float variable. 

Remember that floating-point numbers are stored in a different format than binary integers. To store a double value in a float variable, Java would have to recompute both the mantissa and the exponent portion of the number, which it does not do automatically. Because of this, when you want to store a literal floating-point number in a float variable, you must add an F at the end of the number, as in our third example. You may use either an uppercase F or a lowercase f.

Back to Top

Exponential Notation

You can also write floating-point literals using decimal [base 10], exponential notation. To do this, you write down the first digit of your number, followed by a decimal point and all of the rest of your digits. 

For instance, to write the number 23456.789 in exponential notation, you would start by writing:

2.3456789
Immediately following this, you write an e or an E--making sure you don't add any spaces--followed by the number of places required to move the decimal when the number is "decoded." 

In our example, we need to move the decimal to the right by four places to reconstruct our original, so the finished number would look like this:

2.3456789E4
If the number you're working with is small--say .000004572--you do he same thing. The only difference is you use a negative exponent which says, "move the decimal point to the left, not the right". To write the preceeding number in exponential notation you'd write:

4.572e-6

Back to Top

Which Should You Use?

Choosing between using scientific notation and decimal notation when you write your literals is usually not difficult. If you have very large numbers or very small numbers, you should use scientific notation. If you have "numbers of everyday experience", you probably want to use decimal notation.

That's it for the numeric primitive types. You'll meet them again quickly enough when you complete the "Expressions" Lesson. Until then, let's turn our attention to Java's character primitives.

Something to Talk About

Numbers are pretty straightforward, right? Well, here are some questions to see how well you really understand Java's primitive numeric types.

I have the number 12345678901234567890.0. If I store the number in a float variable, what value will be stored? What value will be stored if I store the same value in a double variable?

Write a declarations for variables to store the following values, using the best primitive type for the particular value. In a comment following the declaration, say why you chose that type.

  • The number of miles between any two cities in the US.
  • The number of stars in the universe.
  • The average number of children per family in the US.
  • The number of colors used by your display video adapter.
  • The number of colors in a box of crayons.

Please continue to the next section of this lesson.

 

Back to Top

 

Content Developed by Stephen Gilbert, Licensed under a Creative Commons License
Published by the Sofia Open Content Initiative
© 2004 Foothill-De Anza Community College District &The William and Flora Hewlett Foundation