### CoSTARS Note #6: Doubles , Booleans, and Chars (And Other Primitives, too!)

Reminder:  when we are not using graphics, we start from this gray/green code:

```public class SampleCode {
public static java.util.Scanner scanner = new java.util.Scanner(System.in);
public static void main(String[] args) {
// place your code here!

}
}```

1.  Doubles
(or "Floating Point Numbers")
(or "Numbers with Decimals")

So far, all our variables have been integers.  Java supports other types of variables, too.  For example, you can use floating point numbers in Java.  These are numbers with decimals, such as 3.14159.  These values are not integers, and so cannot be stored in integer variables.  For example, this code will not work:
int x = 3.14;  // WILL NOT WORK!
When we declare the variable x to be of type "int", we are requiring that it only hold integer values.  So it cannot hold the value 3.14.

So we need a new type of variable that can hold floating point numbers.  For historical reasons, this is called a "double".  Despite the peculiar name, this is a handy variable type.  It works like this:
double d = 3.14;

Doubles differ from ints in several ways:

1)  Doubles can hold decimal values (like 3.14) and ints cannot.
In particular, doubles can hold fractions between 0 and 1 (like 0.5), and ints cannot.  For example:
double d = 1;
int x = 1;
x /= 2;
d /= 2;
System.out.println(x);  // prints 0

System.out.println(d);  // prints 0.5

2)  Doubles have a much larger range than ints.
The largest positive int is about 2 billion, but the largest positive double is about 10308 (which is way larger!).  For example:
System.out.println(Integer.MAX_VALUE);  // prints 2147483647
System.out.println(Double.MAX_VALUE);   // prints 1.7976931348623157E308

Also, the smallest positive int is 1, but the smallest positive double is about 10-324, also written as 1/10324 (which is very close to zero).  For example:
System.out.println(Double.MIN_VALUE);   // prints 4.9E-324

3)  Doubles are approximate and ints are exact.
Unlike ints, Java stores doubles as approximate values rather than exact values.  For example:
double d = (29 / 7.0) * 7.0;
Here, the value "d" seems like it should equal 29 (seeing as we divide 29 by 7 and then multiply the result by 7 again).  But this is not quite what happens, as we see when we print out the value:
double d = (29 / 7.0) * 7.0;
System.out.println(d); // prints 29.000000000000004, not 29
So we see that Java introduced a very small round-off error of 0.000000000000004.  This is very small indeed!  But it is non-zero, and leads to a serious problem when comparing doubles, as we will discuss in a moment (once we learn about boolean variables).

4)  Doubles have some special constant values that ints do not have.
Unlike ints which have no special constant values, doubles have three special constant values.  Two of these represent positive and negative infinity.  These are Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY.  The third value is Double.NaN, which stands for "Not a Number", which is the result of various illegal operations. Perhaps strangely, these three values are all valid values for double variables to hold.  For example:
double d1 = Double.POSITIVE_INFINITY;
double d2 = Double.NEGATIVE_INFINITY;
double d3 = Double.NaN;
System.out.println(d1+d1);  // prints out Infinity (which is positive infinity)
System.out.println(d2+d2);  // prints out -Infinity (which is negative infinity)
System.out.println(d1+d2);  // prints out NaN (not a number)
System.out.println(d1+d3);  // prints out NaN (not a number)
Do you see why each of the last two lines prints out NaN?

5)  Doubles do not throw exceptions the same way ints do.
As you know, in integer math, Java throws an exception when you divide by zero.  For example:
System.out.println(5/0);
This code will throw a DivisionByZeroException, as expected.  However, this is not what happens with double math:
System.out.println(5.0/0.0);
Instead of complaining about dividing by zero, this actually succeeds, and prints out Infinity!  Why?  Well, if you divide 5.0 by a very small number, say, 0.00000001, you will get a very large number, like 500000000.  The smaller the denominator, the larger the result.  By this logic, if we divide by zero, we should get infinity, which is just what Java does!  And by the same logic:
System.out.println(-5.0/0.0);
prints out -Infinity.

There are other cases where Java gives surprising results:
System.out.println(0.0/0.0);
In this case, Java has two different rules to follow:  0.0 divided by any number equals 0, but any (non-negative) number divided by 0.0 equals infinity.  Not knowing whether to evaluate to 0 or infinity, Java does neither, instead evaluating to NaN ("not a number").  Fascinating!

More practice:

• Problem #1:  Java prints double values and int values differently -- even when a double value is an integer.  Run the following code and explain its output:
double d = 1;
int x = 1;
System.out.println(x);

System.out.println(d);

• Problem #2:  Note that Java performs integer math when both values are integers.  However, if either value is a double, Java converts the other value to a double if necessary and performs double math.  Using this fact, predict what the following code will print out.  Write down your answers.  Then (and only then) run the program and compare its output to your answers.

1. System.out.println(1 + 2);
System.out.println(1.0 + 2);
System.out.println(1 + 2.0);
System.out.println(1.0 + 2.0);

2. System.out.println(5 / 2);
System.out.println(5.0 / 2);
System.out.println(5 / 2.0);
System.out.println(5.0 / 2.0);

3. System.out.println(1 + 2 * 3 / 4);
System.out.println(1 + 2 * 3 / 4.0);
System.out.println(1.0 + 2 * 3 / 4);
System.out.println(1 + 2.0 * 3 / 4);

4. ```System.out.println(6 + 5 / 4 * 3);
System.out.println(6.0 + 5 / 4 * 3);
System.out.println(6 + 5.0 / 4 * 3);
System.out.println(6 + 5 / 4.0 * 3);
System.out.println(6 + 5 / 4 * 3.0);```
5. ```double d1 = 3;
double d2 = 5 / d1;
double d3 = 5 / 3;
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);```
6. ```double d1 = 5.0/0.0;
double d2 = 0.0 - 2*d1;
double d3 = d2 / d1;
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);```

2.  Booleans

So far, all our variables have been numbers -- ints or doubles.  Java supports non-numeric variables, too.  The simplest non-numeric type is "boolean".  A boolean variable can hold one of two values:  true or false.  For example:
boolean b1 = true;
boolean b2 = false;
System.out.println(b1); // prints true
System.out.println(b2); // prints false

Equality Operators

Just as we have operators that produce numeric results, like +, -, *, /, and %, we also have operators that produce boolean results (true or false).  For example, we can use an equality operator to test if ints are equal using "==" (two equal signs in a row):
boolean b1 = (1 == 1);  // does 1 equal 1?  Yes!
boolean b2 = (1 == 2);  // does 1 equal 2?  No!
System.out.println(b1); // prints true
System.out.println(b2); // prints false

And just as we can test whether two ints are equal using ==, we can use the other equality operator to test of ints are not equal using "!=" (an exclamation followed by an equals sign):
boolean b1 = (1 != 1);  // does 1 NOT equal 1?  No!
boolean b2 = (1 != 2);  // does 1 NOT equal 2?  Yes!
System.out.println(b1); // prints false
System.out.println(b2); // prints true

Equality Operators and Booleans

While it may seem strange at first, we can use equality operators to compare two boolean values in the same way that we compare two numeric values.  For example:
boolean b1 = (true == true);  // does true equal true?  Yes!
boolean b2 = (true == false); // does true equal false?  No!
System.out.println(b1); // prints true
System.out.println(b2); // prints false

Equality Operators and Doubles

As we noted above, doubles are stored as approximate values.  This can lead to very strange behaviors when doubles are combined with equality operators.  For example:
double d1 = (28.0 / 7.0) * 7.0;
double d2 = (29.0 / 7.0) * 7.0;
boolean b1 = (d1 == 28.0);
boolean b2 = (d2 == 29.0);
System.out.println(b1); // prints true
System.out.println(b2); // prints false
It is not surprising that b1 is true -- after all, to find d1, we take 28.0, divide it by 7.0, and multiply the result by 7.0.  It seems natural that we obtain 28.0 as our result, so we expect (d1 == 28.0) to be true.  And so it is!

However:  when we follow the same logic using 29.0 rather than 28.0, we still expect that (d2 == 29.0), but it does not!  Instead, d2 equals 29.000000000000004.  This is very close to 29.0, but not exactly 29.0.  And so (d2 == 29.0) is false!

Let's see this same example presented slightly differently:
double d = 28.0;
System.out.println(((d / 7) * 7) == d);  // prints true
d = 29.0;
System.out.println(((d / 7) * 7) == d);  // prints false

So we see that the expression (((d/7)*7) == d) is true for some values of d and false for others.  How confusing!  Indeed, it is very confusing and almost always a bad idea to use equality operators with double values.  So have this rule:

Rule:  Never use equality operators with double values!

Relational Operators

Besides testing for equality or inequality, we can compare numbers based on their order using the relational operators <, >, <=, and >=.  For example:
System.out.println(1 <  0); // prints false
System.out.println(1 >  0); // prints true
System.out.println(1 <= 0); // prints false
System.out.println(1 >= 0); // prints true

System.out.println(1 <  1); // prints false
System.out.println(1 >  1); // prints false
System.out.println(1 <= 1); // prints true
System.out.println(1 >= 1); // prints true

System.out.println(1 <  2); // prints true
System.out.println(1 >  2); // prints false
System.out.println(1 <= 2); // prints true
System.out.println(1 >= 2); // prints false

Boolean Operators (aka Logical Operators)

We can also combine boolean values using boolean operators (also called logical operators).  You may recall that all logical expressions can be constructed using some combination of AND's, OR's, and NOT's.  For this reason, Java includes these three operators:  the AND operator (&& -- two ampersands), the OR operator (|| -- two vertical bars), and the NOT operator (! -- an exclamation mark).  These work as expected, according to the rules of logic that we previously covered.  For example:
System.out.println(true && true);   // true  AND true  is true
System.out.println(true && false);  // true  AND false is false
System.out.println(false && true);  // false AND true  is false
System.out.println(false && false); // false AND false is false

System.out.println(true || true);   // true  OR true  is true
System.out.println(true || false);  // true  OR false is true
System.out.println(false || true);  // false OR true  is true
System.out.println(false || false); // false OR false is false

System.out.println(!true);          // NOT true is false
System.out.println(!false);         // NOT false is true

We can combine equality, relational, and boolean operators in interesting ways.  For example:
int n = scanner.nextInt();
boolean isOdd = (n % 2 != 0);
boolean isPositive = (n > 0);
boolean isOddPositive = isOdd && isPositive;
System.out.println(isOddPositive);
This code reads in an integer, and prints out "true" if that integer is both positive and odd (as opposed to even), and false otherwise.  Study it carefully.

More practice:

• Problem #3:  Predict what the following code will print out.  Write down your answers.  Then (and only then) run the program and compare its output to your answers.

1. System.out.println(1 <= 2 + 3 % 4);

2. int a=1, b=3;
System.out.println((a < b/2) || (b >= a*3));

3. System.out.println(1/2 == 0.5);
System.out.println(1/2 == 0.0);
System.out.println(1/2 == 0);

4. int c=5, d=4;
boolean e = (d >= (c - c/d));
boolean f = (d/c == 0.8);
boolean g = (e || f);
System.out.println(e);
System.out.println(f);
System.out.println(g);
System.out.println((!e || f || g) && (!g || f));

• Problem #4:  In a recent example, we saw that (n % 2 != 0) evaluates to true if n is odd and false otherwise.  Provide 3 different expressions that do the same thing -- that is, that evaluate to true if n is odd and false otherwise.  Note that your expressions may be similar to each other, and to the given expression, but they must differ in at least some way.  Also note that (n % 2) equals -1 if n is a negative odd number.  For some bonus points, provide even more examples of expressions that evaluate to true if and only if a number is odd.

• Problem #5:  Briefly explain how the following 4 lines of code demonstrate one of DeMorgan's Laws:
System.out.println(!(true && true) == (false || false));
System.out.println(!(true && false) == (false || true ));
System.out.println(!(false && true) == (true || false));
System.out.println(!(false && false) == (true || true ));
Also, modify the code so that it demonstrates another one of DeMorgan's Laws.

• Problem #6:  Write Java programs that work as described, except that each print statement may only print out the value of one suitably-named boolean variable.

1. Write a program that reads in an integer and prints true if that integer is even and false otherwise.

2. Write a program that reads in an integer and prints true if that integer is divisible by 5 and false otherwise.

3. Write a program that reads in an integer and prints true if that integer is a positive number that is not divisible by 2, 3, or 5, and false otherwise.

4. Write a program that reads in two integers and prints out true if exactly one integer is positive and the other is non-positive, and false otherwise.  Note that zero is not positive and not negative, but it is non-positive.  Here, you may not use an equality operator (==, !=).

5. Rewrite the preceding program without using boolean operators (&&, ||, !).  Instead, use two boolean variables to indicate whether each integer is positive, then use an equality operator (==, !=) to compare these boolean variables.

• Problem #7:  Write a program that reads in an integer and prints true if that integer represents a leap year and false otherwise.  A year is a leap year if it is divisible by 4, except that years divisible by 100 are not leap years unless they are also divisible by 400.  So, for example, 1900 was not a leap year because it is divisible by 100 but 2000 was a leap year because, whereas it is divisible by 100, it is also divisible by 400.

• Problem #8:  Write a program that reads in two doubles and prints true if they are almost equal -- that is, if their difference is less than a very small value, which we will call "epsilon" -- and false otherwise.  You can set epsilon equal to 0.00000001.  Be sure that your program works whether the larger number is entered first or second.  Note that you may not use Math.max, Math.min, or Math.abs, which are methods we will soon learn about and which otherwise would be quite helpful here.

3.  Scanning and Printing Doubles and Booleans

We already saw in the examples above that you can print the values of doubles and booleans using System.out.print and System.out.println  What about reading these values from the user?

Just as we can use the scanner.nextInt to read ints, we can use scanner.nextDouble to read doubles and scanner.nextBoolean to read booleans.  For example:
System.out.print("Enter an integer: ");
int x = scanner.nextInt();
System.out.println("You entered: " + x);

System.out.print("Enter a double: ");
double d = scanner.nextDouble();

System.out.println("You entered: " + d);

System.out.print("Enter a boolean: ");
boolean b = scanner.nextBoolean();

System.out.println("You entered: " + b);
Run this a few times, and try various values for each type of variable.  Then answer these questions:

• Problem #9: For each of the following, state exactly what occurs, including any exceptions that occur (do not just note that an exception occurs, but list which exception).  What happens when...

1. ... scanner.nextInt() scans the value "3.2"?

2. ... scanner.nextInt() scans the value "3.0"?

3. ... scanner.nextDouble() scans the value "3"?

4. ... scanner.nextDouble() scans the value "Infinity"?

5. ... scanner.nextDouble() scans the value "+Infinity"?

6. ... scanner.nextDouble() scans the value "-Infinity"?

7. ... scanner.nextDouble() scans the value "NaN"?

8. ... scanner.nextDouble() scans the value "INFINITY"? (Is it case-sensitive?)

9. ... scanner.nextBoolean() scans the value "TRUE"?  (Is it case-sensitive?)

10. ... scanner.nextBoolean() scans the value "1"?

4.  The Math Class

So far, we have learned about various math operators, such as +, -, *, /, and %.  Java provides even more built-in math functions by way of the Math class.  For example, Java provides a method Math.sqrt() that computes the square root of its argument:
System.out.println(Math.sqrt(5)); // prints 2.23606797749979

Note that you must preface the method name with "Math." when using these built-in methods in the Math class, so this will not work:
System.out.println(sqrt(5)); // WILL NOT WORK -- must be Math.sqrt(5)

Besides useful methods like Math.sqrt, Java also provides two useful constants -- Math.E (Euler's number, the base of the natural logarithm) and Math.PI:
System.out.println(Math.E);  // prints 2.718281828459045
System.out.println(Math.PI); // prints 3.141592653589793

What built-in Math methods does Java provide?  Here is a table with some of them:

More practice:

• Problem #10:  Predict what the following code will print out -- if you cannot state the exact answer due to random behaviors, at least provide the range of possible answers.  Write down your answers.  Then (and only then) run the program and compare its output to your answers.

1. System.out.println(Math.sqrt(Math.sqrt(2) - 2));
2. System.out.println(Math.ceil(Math.random()));
3. System.out.println(Math.floor(Math.random()));
4. System.out.println(100 + 200*Math.random());
5. System.out.println(Math.pow(Math.ceil(Math.PI),Math.floor(Math.E)));
6. System.out.println((int)Math.round(Math.PI) + (int)Math.round(Math.E));
7. System.out.println(Math.exp(Math.abs(Math.floor(Math.E - Math.PI))));
8. System.out.println(Math.log(Math.exp(Math.PI)));
9. System.out.println((int)Math.round(Math.log10(Math.pow(2,10))));
10. System.out.println(Math.toDegrees(Math.PI));
11. System.out.println(Math.toRadians(135) / Math.PI);

• Problem #11:  Write Java programs that work as described.

1. Write a program that reads in 3 integers and prints out the largest one.

2. Write a program that reads in 3 doubles and prints out the median (the middle value when the values are sorted).
Hint:  find the largest and smallest values (which you should store in variables named "largest" and "smallest" or something similar), and use these to find the median value.

3. Rewrite your program from a previous problem that determines whether two doubles are almost equal, only here you should use Math.min and/or Math.max to simplify the problem.  You may not use Math.abs here.

4. Rewrite your almost equal program once again, only this time using Math.abs to simplify the problem.  You may not use Math.min or Math.max here.

5. Write a program that reads in 2 doubles and prints out the number of integers that lie between them, inclusively.  So if you read in 1.8 and 5.4, you would print out 4, because there are 4 integers in the range [1.8 , 5.4] -- those being 2, 3, 4, and 5.  Similarly, if you read in 1.8 and 2.0, you would print out 1 (just the integer 2 is in the range [1.8 , 2.0]).  Note that your program must work regardless of the order of the inputs, so if you read in 5.4 and 1.8, you should print out 4, and if you read in 2.0 and 1.8, you should print out 1.
Hint #1:  You may want to use Math.max, Math.min, Math.ceil, Math.floor, and (int)Math.round here.
Hint #2:  Be sure that your output is an integer and not a double.  For example, output 1 and not 1.0.

6. Write a program that reads in two doubles, which are the length and width of a right triangle, and prints out the length of the hypotenuse of the triangle.

7. Write a program that reads in the three doubles, which are the lengths of three sides of a triangle, and prints out the area of the triangle using Heron's Formula:  A2 = s(s-a)(s-b)(s-c), where a, b, and c are the lengths of the sides of the triangle, and s (the semi-perimeter) equals half the perimeter of the triangle.

8. Write a program that reads in one double, the radius of a sphere (say in centimeters), and prints out its volume (in cubic centimeters).  Note that the volume of a sphere is (4/3)πr3.

9. Write a program that reads in one double, the volume of a sphere (in cubic centimeters), and prints out its radius (in centimeters).
Hint:  Check your work by entering your answer from this problem into the previous problem and verify that you obtain the original volume!

10. Write a program that reads in two doubles, the radius in kilometers of a planet and its average density in grams-per-cubic-centimeter, and prints out the approximate mass in kilograms of the planet.
Hint #1:  There are 1000 grams in a kilogram.  Also, there are 100 centimeters in a meter, and 1000 meters in a kilometer, so there are 100,000 centimeters in a kilometer.  Thus, there are 100,0003, or 1015 (one quadrillion), cubic-centimeters in a cubic-kilometer.
Hint #2:  You may find this table useful:
 Planet Density(g/cc) Radius (km) Mercury 5.43 2,440 Venus 5.20 6,051 Earth 5.52 6,378 Moon 3.34 1,738 Mars 3.91 3,397 Jupiter 1.33 71,492 Saturn 0.69 60,268 Uranus 1.32 25,559 Neptune 1.64 24,764 Pluto 2.00 1,160

11. According to Isaac Newton, your weight on some planet is proportional to the ratio mplanet/r2. where mplanet is the mass of the planet, and r2 is the square of the radius of the planet.  So, if the mass of a planet is twice that of Earth (and the radius is the same as Earth's), then your weight doubles on that planet, whereas if the radius is twice that of Earth (and the mass stays the same as Earth's), then your weight is divided by 4 when visiting that planet.  Write a program that reads in two doubles, the radius in kilometers of a planet and its average density in grams-per-cubic-centimeter, and prints out how many times heavier you would be when standing on that planet as compared to standing on the surface of the Earth.  We use the letter "g" (lowercase) to represent the force of gravity on the surface of the Earth, so express your answer as a multiple of "g".
Hint #1:  start with your solution to the previous problem!
Hint #2:  if you enter Earth's radius and density, your program should output "1.0 * g".
Hint #3:  find a table on the web that lists your weights on different planets to verify that your program works correctly.

5.  The Random Class

While Math.random() may be useful in some cases, Java includes a more powerful collection of random methods in the Random class.  For example, this class not only generates random doubles between 0 and 1, but also random integers between 0 and a value of your choosing, and even random booleans (which are great for simulated coin flips!).

In order to use the Random class, however, we need to add a new line of gray code:
public static java.util.Random random = new java.util.Random();

So here is all our gray/green code including this new line:
public class SampleCode {
public static java.util.Scanner scanner = new java.util.Scanner(System.in);
public static java.util.Random random = new java.util.Random();
public static void main(String[] args) {
// place your code here!

}
}

Starting from this gray/green code, here is a simple example that prints out a random double between 0 and 1:
System.out.println(random.nextDouble());

Compile this code and run it.  You should notice three differences with this new approach:

1. The Random class requires the new line of gray code;

2. The Random method is prefaced with "random." rather than "Math." (and the "random" is lowercase whereas "Math" is capitalized); and

3. This particular Random method is named "nextDouble" whereas the equivalent Math method is named "random"

With that in mind, here are some useful methods from the Random class:

 Method Description random.nextDouble This method is basically equivalent to Math.random(). Returns a random number between 0 (inclusive) and 1 (exclusive).  For example:       System.out.println(random.nextDouble()); This prints a different random double between 0 and 1 every time you run it. random.nextBoolean Returns a random boolean (true or false).  For example:       System.out.println(random.nextBoolean()); This prints either true or false, with equal probability. random.nextInt There are two forms of this method. The first form takes no argument and returns a random integer between the smallest and largest possible values -- that is, roughly between negative 2 billion and positive 2 billion.  For example:       System.out.println(random.nextInt()); The second form takes an argument, and returns a random integer between 0 (inclusive) and its argument (exclusive).  This is a very handy method for many tasks.  For example:       System.out.println(random.nextInt(6)); This prints out a random number between 0 and 5 (because 6 is exclusive -- it will never occur).  Often, we want a random number between 1 and some value.  For example, to randomly choose a value when rolling a 6-sided die.  For this, we just add 1 to the result, as such:       System.out.println(1 + random.nextInt(6)); This prints out a random number between 1 and 6, as desired. random.setSeed It can be tricky to debug a program that uses random numbers, because its behavior may change every time you run it.  Java provides a way around this.  You can use the setSeed method to force Java to use the same sequence of random numbers each time you run your program.  This method takes one argument, an integer, thus allowing you to try different sequences of numbers that seem random and yet are predictable.For example:       System.out.println(random.nextInt(1000));       System.out.println(random.nextInt(1000));       random.setSeed(0);       System.out.println(random.nextInt(1000)); // always the same!       System.out.println(random.nextInt(1000)); // ditto! Each time you run this, the first two numbers will indeed be random, but the second two numbers will always be the same!  Try it a few times to confirm this.

Some practice:

• Problem #12: Write Java programs that work as described, except that you may not use Math.random here.

1. Write a program that prints out a random double between 0 and 5 (exclusive).

2. Write a program that prints out a random double between 5 and 10 (exclusive).

3. Write a program that prints out a random integer between 0 and 5 (exclusive).

4. Write a program that prints out a random integer between 1 and 5 (inclusive, so it can print out 5 sometimes).

5. Write a program that prints out a random integer between 5 and 10 (inclusive).

6. Write a program that reads in a positive integer n and prints out a random number between 0 and n inclusive.

7. Write a program that uses nextBoolean to simulate flipping a coin 3 times, and print out true if all 3 coin flips were heads and false otherwise.

8. Write a program that uses the form of nextInt that does not take an argument, and call this method 3 times, and print outs true if all 3 random integers are negative.  Do you think we could use nextInt in this way to simulate a coin toss?

6.  Chars

Besides ints and doubles, Java includes another numeric type, char, that is mainly used to represent characters in Strings.  We will use chars much more once we learn about Strings (coming soon!), but here we will begin to study them.

To assign a specific character to a char variable, we enclose that character in single-quotes (not double-quotes), like this:
char c1 = 'A'; // assigns uppercase-A to variable c1
char c2 = 'B'; // assigns uppercase-B to variable c2
char c3 = 'a'; // assigns lowercase-a to variable c3
System.out.println(c1);  // prints A
System.out.println(c2);  // prints B
System.out.println(c3);  // prints a

Chars are special kinds of integers, actually, so we can compare chars using equality and relational operators.  The comparison works alphabetically, so 'A'<'B', except that all uppercase letters occur before any lowercase letters, to 'B'<'a', as we see here:
char c1 = 'A';
char c2 = 'B';
char c3 = 'a';
System.out.println(c1 < c2);  // prints true, as 'A' < 'B'
System.out.println(c1 < c3);  // prints true, as 'A' < 'a'
System.out.println(c3 < c2);  // prints false, as 'B' < 'a'

Not only are chars special kinds of integers, we can even find the specific integer value assigned to any particular character.  Java uses Unicode, which is a code that converts characters to numbers.  The Unicode value for 'A' is 65, 'B' is 66, 'C' is 67, and so forth.  Lowercase 'a' is 97, 'b' is 98, 'c' is 99, and so on.  We can see the Unicode values of chars by first assigning those chars into integers and then printing them out, like this:
int i1 = 'A'; // we are assigning the char 'A' into an integer variable
int i2 = 'B'; // same for 'B'
int i3 = 'a'; // same for 'a'
System.out.println(i1); // prints 65, the Unicode value of 'A'
System.out.println(i2); // prints 66, the Unicode value of 'B'
System.out.println(i3); // prints 97, the Unicode value of 'a'

Even though we can find the Unicode values for chars, we very rarely need them.  For example, say we need to check if a character is an uppercase letter.  This is true if its Unicode value is at least 'A' (65) and not greater than 'Z' (90).  We can perform this check as follows:
((c >= 'A') && (c <= 'Z')) // evaluates to true if c is an uppercase letter and false otherwise
Here we will use this code to confirm that 'A' is an uppercase letter and 'a' and '\$' are not uppercase letters:
char c = 'A';
System.out.println((c >= 'A') && (c <= 'Z')); // prints true, as 'A' is uppercase
c = 'a';
System.out.println((c >= 'A') && (c <= 'Z')); // prints false, as 'a' is not uppercase
c = '\$';
System.out.println((c >= 'A') && (c <= 'Z')); // prints false, as '\$' is not an uppercase letter
Note that this code does not require that we know the actual values assigned to 'A' or 'Z'.

Some practice:

• Problem #13:  Predict what the following code will print out.  Write down your answers.  Then (and only then) run the program and compare its output to your answers.
Hint:  The Unicode value of the character '0' (zero) is 48.

1. char c = 'D';
int i = c;
System.out.println(c);
System.out.println('D');
System.out.println(i);

2. System.out.println('z' > 'A');
System.out.println('Z' > 'a');

3. int i = '0';
System.out.println(i);
i = '1';
System.out.println(i);
i = '2';
System.out.println(i);

4. int i = 'Z';
System.out.println(i);
i = 'z';
System.out.println(i);
i = '9';
System.out.println(i);

5. System.out.println('5' < 'A');
System.out.println('5' < 'a');

6. char c1 = '2';
char c2 = '5';
int i1 = (c1 - '0');
int i2 = (c2 - '0');
int i = 10*i1 + i2;
System.out.println(i1);
System.out.println(i2);
System.out.println(i);

• Problem #14:  Write Java programs that work as described.  For each program, you will first assign some value to a variable 'c' of type char.  Your program must work for any value assigned to that variable.
1. Write a program that prints true if c is either an uppercase or a lowercase letter, and false otherwise.

2. Write a program that prints true if c is a decimal digit (that is, one of '0', '1', '2', ... , '9') and false otherwise.

3. Write a program that prints true if c is a hexadecimal digit and false otherwise.  The hexadecimal digits include all the decimal digits ('0', '1', ..., '9'), but also include the first 6 letters (from 'A' to 'F' for uppercase, or from 'a' to 'f' for lowercase).

7.  Other Primitive Types

So now we know about ints, doubles, booleans, and chars.  In Java, these are called primitive types.  This is because while they have a value, we cannot use them to call any methods.  By contrast, a reference type can be used to call certain methods defined for that type.  We have already seen some reference types.  Consider the scanner -- we can use that object to call the nextInt() and nextDouble() methods among others.  Or consider the page variable in our graphics programs --- we can use that object to call the fillRect() and setColor() methods among others.  With primitive types, however, we cannot call any methods on them.  So we cannot have code like this:
double d1 = 5;
double d2 = d1.sqrt();  // Will not compile!
This code will not work because d1 is not an object, but a primitive type, and so we cannot call any methods on it.  Of course, we can compute the square root of d1, but we do it like this:
double d1 = 5;
double d2 = Math.sqrt(d1);

Here, we are not calling a method on d1, but providing the value of d1 as an argument to a method.  Be sure you understand this distinction!

Are there other primitive types?  Yes.  In fact, there are 8 primitive types in Java:  ints, doubles, booleans, chars, bytes, shorts, longs, and floats.  The AP exam only covers the first four types (ints, doubles, booleans, and chars), but you should be familiar with all 8 types, so we will describe them here:

This may be daunting at first, but a few observations make it much easier to digest:

1. Booleans are not numeric.
Booleans can only be true or false, and not a number -- and all the other primitive types are numbers.

2. Only floats and doubles are non-integer numeric primitives.
All other numeric types are restricted to integers.

3. The only unsigned integer type is char.
There are no negative char's, so we say they are unsigned.  All other numeric types are signed -- they include both positive and negative values.

4. We can find the min/max values with these rules:

1. Signed values (all but char's) use 1 bit for the sign (positive or negative).
So a k-bit signed value must use 1 bit for the sign, leaving (k-1) bits for the values.

2. Unsigned values (just char's) do not use an extra bit for the sign
So a k-bit unsigned value can use all k bits for the value.

3. Even though 0 is not positive or negative, it is included with the positives.
So there is one extra negative value than positive values in signed numerics.

4. k bits can have 2k different values.

5. Hence, we have these rules for integer types:

1. k unsigned bits range from 0 to 2k-1.

2. k signed bits range from -2(k-1) to +2(k-1)-1.

6. We can apply these rules to each integer type like this:

1. Bytes are 8-bit signed numerics, so k=8, and they range from -27 to +27-1.

2. Shorts are 16-bit signed numerics, so k=16, and they range from  -215 to +215-1.

3. ints are 32-bit signed numbers, so k=32, and they range form  -231 to +231-1.

4. Longs are 64-bit signed numbers, so k=64, and they range form  -263 to +263-1.

5. Chars are 16-bit unsigned numerics, so k=16, and they range from  0 to +216-1.

Some practice:

• Problem #15:  Predict what the following code will print out.  Write down your answers.  Then (and only then) run the program and compare its output to your answers.

Some of these results may be surprising to you -- be sure you take the time to understand why they work as they do!

1. int iMin = Integer.MIN_VALUE;
int iMax = Integer.MAX_VALUE;
System.out.println(iMax + 1);  // overflow!
System.out.println((iMax + 1) == iMin);
System.out.println(iMin - 1);  // also overflow!
System.out.println((iMin - 1) == iMax);
System.out.println(iMax + iMin);

2. double dMax = Double.MAX_VALUE;
System.out.println((dMax + 1.0) == dMax);  // overflow
System.out.println((dMax * 2.0) == Double.POSITIVE_INFINITY); // another kind of overflow
double dMin = Double.MIN_VALUE;
System.out.println((dMin / 2.0) == 0.0);  // underflow

3. int minC = Character.MIN_VALUE;
int maxC = Character.MAX_VALUE;
System.out.println(minC);
System.out.println(maxC);

System.out.println(Byte.MIN_VALUE);
System.out.println(Byte.MAX_VALUE);

System.out.println(Short.MIN_VALUE);
System.out.println(Short.MAX_VALUE);

System.out.println(Long.MAX_VALUE > Integer.MAX_VALUE);
System.out.println(Long.MIN_VALUE < Integer.MIN_VALUE);

System.out.println(Double.MAX_VALUE > Float.MAX_VALUE);
System.out.println(Double.MIN_VALUE < Float.MIN_VALUE);

8.  Converting Types

We often must convert between different numeric types, say converting a char into an int, or an int into a double.  Here we discuss the various types of conversions, and how to perform them.

Widening versus Narrowing Conversions

As we saw, chars can be between 0 and +65535, and ints can be between about -2 billion and +2 billion.  Thus, we can convert any legal char value into a legal int value.  For example:
char c = 'A';           // assigns a char value (65, to be exact)
int i = c;              // converts the char into an int
System.out.println(i);  // prints 65

Great! However, the opposite is not true -- there are many legal int values that are illegal char values.  For example, say we are converting the int value -5 to a char.  Seeing as the smallest char value is 0, this is impossible.  So the compiler will not let you do it!  Here is the code that shows this failure:
int i = -5;            // assigns an int value
char c = i;            // fails to convert the int into a char (will not compile)
System.out.println(c);

In fact, this code will fail for any value assigned to the int variable, even if it within the legal range of a char.  For example:
int i = 'A';           // assigns an int value (65, which is a legal char value)
char c = i;            // STILL fails to convert the int into a char (will not compile)
System.out.println(c);

So we see that the compiler will convert chars into ints, but it will not convert ints into chars.

Because every char value is a legal int value, we say that ints are wider than chars (or, if you prefer, that chars are narrower than ints).  A conversion from a narrower type to a wider type is called a widening conversion (and the other way around is a narrowing conversion).

The key:  Java will automatically perform widening conversions, but will not automatically perform narrowing conversions.

So:  which conversions are widening, and which are narrowing?  We already saw that chars are narrower than ints, because some legal int values are not legal char values.  To answer this question, we will construct the following table which shows the ranges for each primitive numeric type:

 value double float long int char short byte -1.8 x 10308 -3.4 x 1038 about -9 billion billion about -2 billion -32768 -128 0 +127 +32767 +65535 about +2 billion about +9 billion billion +3.4 x 1038 +1.8 x 10308

This table clearly shows which types are narrower than other.  A type is narrower than a second type if it is missing a bar in this table where the second type has a bar.  From this, we see that:

• doubles are wider than all other types

• floats are wider than all other types except doubles

• longs are wider than all other types except floats and doubles

• ints are wider than chars, shorts, and bytes

• chars are not wider than any other type!

• shorts are wider than bytes (but not chars)

• bytes are not wider than any other type!

One surprising result is that two types can each be narrower than the other!  For example, chars are narrower than bytes but bytes are also narrower than chars.

Types of Conversions

Here we cover the four different ways in which primitive type conversions may occur.

1)  Assignment Conversions

An assignment conversion occurs when we assign a value of one type into a variable of another type.  For example:
int i = 5;
double d = i;   // Assignment conversion!
This example converts the integer 5 into the double 5.0 and then assigns that value (5.0) to the double d.

Assignment conversions must be widening conversions (such as from int to double).  Assignment conversions cannot be narrowing conversions (such as from doubles to ints).  For example:
double d = 5.0;
int i = d;  // Fails:  narrowing assignment conversion

2)  Method Invocation Conversions

A method invocation conversion occurs when we assign an argument of one type to a parameter of another type in a method call.  For example:
int i = 5;
double d = Math.sqrt(i);   // Method invocation conversion!
This example assigns the integer value 5 to the only parameter of the Math.sqrt method, but that method expects its argument to be a double, so Java first converts the integer (5) into a double (5.0), and then calls the method.

Method invocation conversions must be widening conversions (such as from int to double).  Method invocation conversions cannot be narrowing conversions (such as from doubles to ints).  For example:
page.fillRect(0,0,10,20.5); // Fails:  narrowing method invocation conversion
The fillRect method expects 4 integers for the left, top, width, and height.  Here, we provided a double for the height, and doubles are wider than integers, so this fails.

3)  Promotion Conversions

A promotion conversion occurs when we perform arithmetic on values with different types.  For example:
double d = 1.5 + 2;   // Promotion conversion!
To add 1.5 (a double) and 2 (an integer), Java first makes these values have the same type by promoting 2 from an integer to a double, then adding 1.5 and 2.0, the result of which is a double.  Notice that there is no assignment conversion here -- the right-hand-side is already a double before it is assigned to the double

Because Java always promotes the narrower type to the wider type, promotion conversions are always widening conversions (such as from int to double).

4)  Casting Conversions

The previous three cases are all automatic:  when necessary, Java automatically performs a widening conversion.  What about narrowing conversions?  Java will never perform these automatically.  But sometimes we must perform a narrowing conversion.  In this case, we must explicitly direct the compiler to do the conversion by casting to the narrower type.  For example:
double d = 5.0;
int i = (int) d;        // Legal narrowing conversion with casting

System.out.println(i);  // prints 5
This example compiles and runs without errors.  How does it work?  On the second line, we precede the value d with "(int)" -- that is, the type int enclosed in parentheses.  This awkward syntax is how we cast the double into an int.  The result of  (int)d   is an int where the double d has been truncated.  That is, its value after the decimal point is simply ignored.  For example:
System.out.println((int)4);    // prints 4 (not 4.0)
System.out.println((int)4.9);  // prints 4 (not 5)
System.out.println((int)4.5);  // prints 4 (not 5)
System.out.println((int)-4.9); // prints -4
Here we see that casting to an integer is not the same as rounding, nor taking the floor nor ceiling.  In all cases, casting to an integer will simply truncate a value after the decimal point.

What happens if we cast a value that is out of range?  For example:
System.out.println((byte)200);  // overflow!  prints -56
Here, we try to cast the value 200 from an int into a byte, but the largest byte is +127.  This is a case of overflow, and so the result is bogus.  In this case, the bogus result happens to be -56 (which, of course, is not equal to 200).  In general, if you cast a value that is outside the range of the target type, the result will be incorrect.

As an aside, we now can understand why we use "(int)" in a call to Math.round.  It turns out that Math.round is the only Math method that takes a double parameter and yet returns a long result.  Since converting a long into an int is a narrowing conversion, we cannot do this automatically, so we must cast the result to an int.  This is precisely what we do when we precede the call with "(int)", as such:
int i = (int)Math.round(2.50); // rounds 2.5 to the long 3, then casts to the int 3
System.out.println(i);          // prints 3

Illegal Conversions

There are some conversions that Java will not perform, even with casting.  In particular, Java will not convert between boolean values and numeric values.  For example:
boolean b = true;
int i = (int) b;  // Fails:  illegal conversion from boolean to int

Here, we try to convert the boolean value "true" into an integer value.  We may think this is reasonable (and may expect, say, a value of 1 as a result), but Java will not let us do this.

Other Conversions

Converting character digits to ints

While not strictly conversions in the technical sense, there are other ways in which we may wish to change primitive types.  For example, say we have a char value that we know is a decimal digit ('0', '1', ..., '9'), and we wish to convert it into that decimal value.  This is not the same as directly converting the char into an int, because in that case (as we have already seen) we will obtain the Unicode value for that character.  That is:
char c = '0';
int i = c;
System.out.println(i);  // prints 48

This prints 48, not 0, because 48 is the Unicode value for the character '0'.  To convert to the decimal value 0, we must subtract the Unicode value of '0' (that is, 48).  So we have:
char c = '0';
int i = c - '0';
System.out.println(i);  // prints 0

This seems to work.  Let's try it with the character '3', which has Unicode value 51:
char c = '3';
int i = c - '0';
System.out.println(i);  // prints 3

This prints out 3, and not 51.  So we conclude that we can find the decimal value of a character digit by subtracting '0' and converting to an int.

Converting uppercase to lowercase

As another example, here we convert an uppercase letter to its lowercase equivalent.  This is not a conversion, technically, because we start and end with a char.  Now, because 'A' is 65 and 'a' is 97, we may observe that we just need to add their difference, or 32, to any uppercase letter to get its lowercase equivalent.  However, we wish to do this without using the actual Unicode values, so instead we express the difference as ('a' - 'A').  This is still 32, but does not require us to know that value.  So we have:
char c1 = 'A';
char c2 = c1 + ('a' - 'A');  // fails: illegal narrowing conversion from int to char
System.out.println(c2);
This code will not compile!  What went wrong?  On the second line, when we add c1 and ('a' - 'A'), even though all the values are chars, the result of the operation is an int.  The compiler will not automatically convert this int back into a char when assigning the value to c2.  Fortunately, we know what to do about this:  we simply cast the result back into a char, like this:
char c1 = 'A';
char c2 = (char)(c1 + ('a' - 'A'));  // converts uppercase into lowercase
System.out.println(c2);  // prints 'a'
This seems to work.  Let's try it with uppercase 'Q':
char c1 = 'Q';
char c2 = (char)(c1 + ('a' - 'A'));  // converts uppercase into lowercase
System.out.println(c2);  // prints 'q'
This prints out q, as desired.  So we conclude that we can convert from uppercase to lowercase by adding ('a' - 'A') and casting the result to a char.

Converting booleans to ints

As a final example, we noted that it is illegal to convert from booleans to ints, even with casting.  Yet there are times when we wish to do precisely this.  In this case, we can make use of the tertiary operator (?:).  This is a strange syntax, and your use of it should be limited.  But here is an example that uses the tertiary operator to convert boolean values into integers, using 0 for false and 1 for true:
boolean b = true;
int i = (b ? 1 : 0);    // uses tertiary operator to convert boolean to int
System.out.println(i);  // prints 1
b = false;
i = (b ? 1 : 0);        // once again, now converting false to 0
System.out.println(i);  // prints 0
In general, the tertiary operator takes a boolean value followed by two other values.  If the boolean is true, the operator evaluates to its second value, otherwise it evaluates to its third value.  From this, you should understand how the given example works.  Again, you should use this operator sparingly, if at all.

Some practice:

• Problem #16:  Predict what the following code will print out.  Write down your answers.  Then (and only then) run the program and compare its output to your answers.

Some of these examples will not compile.  Indicate those cases, along with why they will not compile.

1. int i = 32;
char c = 'A' + i;
System.out.println(c);

2. int i = ((Math.sqrt(10) < 3) ? 123 : 456);
System.out.println(i);

3. int i = 11;
double d = i/2.0;
System.out.println(d);
d = i/2;
System.out.println(d);

4. char c = 'A';
double d = c/2;
int i = (int)Math.floor(d);
System.out.println(c);
System.out.println(d);
System.out.println(i);

5. double d = 5.9;
int i = Math.round(d);
System.out.println(i);

6. double d1 = 5.9;
double d2 = (double)(int)d1;
System.out.println(d1);
System.out.println(d2);
System.out.println(d1 == d2);

7. System.out.println(6 / 16 / 4.0);
System.out.println(6 / (16 / 4.0));
System.out.println((int)6 / (16 / 4.0));
System.out.println((int)(6 / 16 / 4.0));
System.out.println((int)(6 / (16 / 4.0)));

• Problem #17:  Write Java programs that work as described.

1. Write one line of Java that demonstrates assignment conversion, promotion conversion, and casting (all three of these on the same line) -- and clearly label each.

2. Write a Java program that reads an int and converts it into a variable of type boolean.  You may assume 0 is false and all other values are true.  Print the value of the boolean.

3. Write a program that reads an int representing a Unicode value and converts it into a variable of type char.  Print the value of the char.  So if your program reads in 65, it should print out uppercase 'A'.

4. Write a program that reads an int between 0 and 9, inclusive, and converts it into a variable of type char -- the character digit equivalent of the input.  Print out both the value of the char as well as its Unicode value.  So if your program reads in 0, it should print out the char '0' followed by the value 48.

carpe diem  -  carpe diem  -  carpe diem  -  carpe diem  -  carpe diem  -  carpe diem