15-100 Lecture 4 (Wednesday, September 7, 2005)

Quick Quiz

Today's five minute quiz was to write java code that performs each of the following five (5) steps. The code can be written in isolation without method or class constructs.

  //Declare two variables, x and y, each of which can hold an integer number
  int x;
  int y;

  
  // Assign 3 to x and 4 to y, respectively.
  x = 3;
  y = 4;


  // Declare a variable, capable of holding an integer, z
  int z;


  // Compute (using within the program, not within your head) 
  // the sum of x and y, and assign the result to z
  z = x + y;


  // Print, to the screen the value of z
  System.out.println (z); 
  

Variable declarations

Last class we began playing with simple arithmetic. We declared new "variables" capable of holding simple numbers -- numbers without decimal components. By declaring a variable, we asked Java to give us a space, that we could identify by name, to store a value. The space is called a "variable" because its value can change or vary.

The declaration looked like this:

int number;

In the example above, "int" is the "type" of variable. The variable can hold only numbers without fractional (decimal) components. The "number" is just the name we are assigning to the variable.

This is a specific example of the more general case of a variable declaration:

type identifier;

By the way, it is often the case that, at the time that a variable is declared, its initial value is known and should be assigned. This is known as "initialization". Below is an example:

int number; number = 3;

This is such a common idiom in programming that the language provides a bit of a shortcut. The two lines can be contracted as follows:

int number = 3;

Integer Types and Limits

So far, we've worked with one type of number, the int. But, there are plenty more. For example, there are actually three different types that represent numbers without mantissas: "short", "int", and "double".

These types don't actually vary in the abstract -- they all store integer numbers. But, they differ in some computer-centric details. Storing information, like storing physical things, takes space. The ability to store bigger numbers requires more space than the ability to store smaller numbers. It is important to realize that when I write, "larger numbers" and "shorter numbers", I am refering to their magnitude, a.k.a. absolute value, not their signed value.

short variables take up less space than int variables, which in turn take up less space than long variables. The flip side is that longs can store bigger numbers than ints, which can, in turn, store larger numbers than shorts.

The details aren't important for this class, but, for reference, here are the sizes and ranges:

short16 bitsabout +/-30,000
int32 bitsabout +/- 2 billion
long64 bitsabout +/- 9 * 1063

Fractional Types

Similarly there are two different types that can represent numbers with decimals: float and double. In terms of their size, they are basically like int and double, respectively.

Below are a couple of examples of declarations:

  float numberWithFraction;
  double widerPreciseNumberWithFraction;
  

Fractional Types and Approximation

It is a bit harder to describe the limits of what the fractional types can store than it was for the integer types. These types are called "floating point types" because, the position of the decimal pace isn't fixed. It "floats". It is farther to the right for larger numbers and farther to the left for smaller numbers. The result is that large numbers can't keep as much detail after the decimal as small numbers.

Consider this example, which illustrates the concept. Notice how the larger numbers store less detail than the smaller numbers.

  .12345
  1.2345
  12.345
  123.45
  1234.5
  12345.
  

So, with the fractional types, double can support some combination of larger numbers and/or more precision.

Funniness in Computation

Given that the amount of storage for a variable is fixed at the time that it is declared, this "floating point" allows the computer to make the best use of the storage. It keeps the most important information that it can, truncating the details it can't hold.

And, this works out well in most cases. It means that, for example, when looking through a telescope, we don't waste memory on millimeters when we care about kilometers. And, similarly, when working through a microscope, we don't waste memory on kilometers when we want the most accurate microscopic measurements that we can get.

The only time we can get into trouble is when we mix numbers of different sizes in arithmetic. This can generate funniness. For example, multiplying a very small number by a very large number, and then dividing by the very small number will likely not result in the original number. This is becuase, when the number becomes large by multiplication, detail is lost. This detail can't be replaced by division. The result is close to, but not the same as, the original.

Type Casting

When we are making use of variables, Java tries to help keep us out of trouble. One way that programmers sometimes get into trouble is by accidentally assigning variables of one type to variables of another type. This can be a problem if the type on the left, the one to which the value is being assigned, is smaller or more restricted, or just outright different than the type on the right.

In general, when the two types are different, it may be for one of three circumstances:

If the types are simply not compatible, Java will not allow the assignment, one cannot assign a char to any of the number types, or vice-versa. There really isn't a good way of interpreting that assignment.

If the type on the left is larger, or more precise, than the type on the right, the assignment is allowed. For example, an int can always be assigned to a long, or an int to a float. This is because no information can possibly be lost in the conversion.

But, if the types are compatible -- but the type on the left is smaller or less precise, the situation is a bit more sticky. The types can be meaningully converted. For example, a double might lose precision if converted to a float. Or, a long might become meaningless if it is muddled cutting it down to fit into an int. But, the programmer might know that the values are in range or that the loss of precision isn't important.

As a result, Java doesn't allow these types -- unless the programmer explicitly acknowledges the conversion via what is known as a "type cast". A type cast is an explicit request by the programmer for a value to be interpreted using a different type. The cast is only good exactly where it is made and doesn't remain in force beyond the single use.

Below is an example of a type cast. Note the conversion is specified using a parenthetical notation:

  double preciseNumber;
  float lessPreciseNumber;

  ...

   preciseNumber = (float) lessPreciseNumber;
  

Mixed Mode Operation

What happens if integer types and floating-point types are mixed in the same computation? For example, an integer co-efficient, a scalar, is applied to a complex fractional computation?

The integer components are viewed as fractional components. The result is of the fractional type. This happens automatically.

Having said that, I will say that I don't like it. I don't like automatic conversions, because they confuse non-expert programmers. For example, the mathematician or engineer who dabbles in coding. And, sometimes they even confuse the expert programmer.

So, instead, when writing mixed-mode computation, I, myself, always add casts. I think it makes the code easier to understand -- need them, or not:

  double numerator;
  double denominator;
  double result;
  int scalar;

  ...

  result = (double)scalar * numerator/denominator;
  

Other types: char and String

I'd also like to mention a couple of other types. First, the char. A char holds a single character, such as a number, letter, or symbol. The value is understood to be a "character", so if, for example, it happens to be a digit -- it cannot be added.

Additionally, there is the String. A String is a collection of characters, such as a sentence, word, or paragraph. Notice that the String begins with a capital "S". This means that it is a "class" not a simple, "primitive" type. We'll talk more about what that means later.

Literals: Notes on Notation

When we include "literal" numbers embedded within our code, if there is no fractional component, they are assumed to be of type int. If we want them to be of type long or short we need to add an "l" or "s", respectively. Similarly, fractional numbers are assumed to be doubles. So, if we want them to be floats, we need to append an "f".

Another option is to use an explicit type cast. In other words, we can leave the literal as an int, but convert it to the right type. I sometimes like this option, because I think it makes the code more readable.

Please consider the examples below:

  int intNumber = 5; 
  long longNumber= 5l;
  short shortNumber= 5s;
  short shortNumber2= (short) 5;

  float floatNumber = 4.5; // Syntax error; 4.5 is a double
  float floatNumber = 4.5f; // OK
  float floatNumber = (float) 4.5f; // Also OK

  
  

As a result, we'll mostly be using int and double types as opposed to long and float types. It saves us the conversions -- and works well for a wide class of problems, also.

Please also keep in mind, from last class, that String literals need to be "quoted" in order that their contents aren't confused with the code.

  String name = "Greg";
  

And, lastly, char literals need to be within 's' single quotes in order that they aren't confused with Strings or ints or other program elements.

  char c = '5';
  

Quiz Next class

As usual, please expect a 5 minute quiz next class. Although for today's quiz you weren't expected to remember the main() method construct -- I won't see a problem with that next time. Beyond that any of the actually coding stuff we've covered so far is fair game.

We're Here To Help!

As always, we're here to help -- please let us know how we can be of service.