Today we look at operations with the floating-point types and what happens when types mix in arithmetic expressions.
double
We have already seen the double
type used to represent
real numbers. The range of values that can be represented by the
type is approximately:
Minimum Value | Maximum Value |
-1.7 × 10308 | 1.7 × 10308 |
A double
occupies 8 bytes of memory and has about
15 significant digits. All of the operators we have seen so far
can be used with double
types.
Operations with double
can overflow. If overflow occurs,
the result is set to a value representing positive infinity (if the value
is positive) or negative infinity (if the value is negative).
The value 0.0 / 0.0
is called NaN
or not a
number. Division by 0.0
with other double
values produces either positive or negative infinity.
public class DoubleClosureExamples { public static void main(String[] args) { // positive overflow double x = 1E200; System.out.println(x * x); // negative overflow System.out.println(-x * x); // NaN double zero = 0.0; System.out.println(zero / zero); // positive overflow (division by 0.0) System.out.println(x / zero); // negative infinity (division by 0.0) System.out.println(-x / zero); } }
Suppose you pay $5.00 for three candy bars costing $1.10 each. How much change should you get back? Write a Java program that solves the problem in two different ways:
(3 * 1.10)
3.30
as the
total cost of the candy barsFor both ways, you should use the subtraction operator to calculate the
final result, and you should use double
s for the monetary
values.
You should have gotten two different answers for the problem on
the previous slide: 1.6999999999999997
and
1.7000000000000002
. The problem occurs because not
all decimal values can be represented exactly using binary floating
point numbers (the internal representation of the primitive
real number types in Java).
Do not use double
to repesent monetary amounts;
instead, convert everything to cents and use int
or long
to perform the calculatioins.
float
The float
type is also used to represent
real numbers but it has a smaller range and less precision than
double
. The range of values that can be represented by the
type is approximately:
Minimum Value | Maximum Value |
-3.4 × 1038 | 3.4 × 1038 |
A float
occupies 4 bytes of memory and has about
7 significant digits. All of the operators we have seen so far
can be used with float
types.
A float
literal is a number followed by
an F
or an f
; for example:
-5F
or -5f
3.1415F
or 3.1415f
2.99792E8F
or 2.99792E8f
Most of the time you would prefer double
over
float
unless lack of memory is an issue.
If you mix types in an arithmetic expression the results can be confusing. You need to apply the precedence rules and associativity rules to determine the order in which the operands are evaluated, and then apply the following set of rules:
double
then
the other operand is promoted to type double
float
then
the other operand is promoted to type float
long
then
the other operand is promoted to type long
int
Assume that for each of the following examples shown in the table below we have the declarations:
double xDouble = 10.0; float xFloat = 5.0F; long xLong = 4L; int xInt = 2; short xShort = 1;
Expression | Evaluation |
xFloat + xDouble |
⇒ ((double) xFloat) + xDouble ⇒ 15.0 |
xFloat * xLong |
⇒ xFloat * ((float) xLong) ⇒ 20F |
xInt - xLong |
⇒ ((long) xInt) - xLong ⇒ -2L |
xShort + xShort |
⇒ ((int) xShort) + ((int) xShort) ⇒ 2 |
+xShort |
⇒ +((int) xShort) ⇒ +1 |
-xShort |
⇒ -((int) xShort) ⇒ -1 |
In an assignment statement involving the primitive numeric types, the Java compiler will promote the right-hand side value (perform a widening conversion) but it will not demote the value (perform a narrowing conversion).
If you have a variable of type:
double
then you can assign to it any value of primitive
numeric typefloat
then you can assign to it any value of primitive
numeric type except double
long
then you can assign to it any value of primitive
integer typeint
then you can assign to it any value of primitive
integer type except long
short
then you can assign to it only values of
type short
byte
then you can assign to it only values of
type byte
char
then you can assign to it only values of
type char
Occassionally, you will want to explicitly perform a widening conversion. For example, suppose you wanted to compute the average of two integers:
public class BadAverage { public static void main(String[] args) { int num1 = 10; int num2 = 11; double avg = (1 / 2) * (num1 + num2); System.out.println(avg); } }
The example computes an incorrect average because the 1
and 2
both have type int
; hence int
division is performed. We want the division to be performed using
floating-point math, so a solution is to cast the 1
or the 2
to a floating-point type.
public class Average1 { public static void main(String[] args) { int num1 = 10; int num2 = 11; double avg = ((double) 1 / 2) * (num1 + num2); System.out.println(avg); } }
In Average1
the literal 1
is
cast to type double
.
public class Average2 { public static void main(String[] args) { int num1 = 10; int num2 = 11; double avg = (1 / (double) 2) * (num1 + num2); System.out.println(avg); } }
In Average2
the literal 2
is
cast to type double
.
public class BadAverageAgain { public static void main(String[] args) { int num1 = 10; int num2 = 11; double avg = (double) (1 / 2) * (num1 + num2); System.out.println(avg); } }
In BadAverageAgain
the expression (1 / 2)
is
cast to type double
, but the value of (1 / 2)
is 0
.
Instead of casting, an easier way to solve this problem is to
use (1.0 / 2.0)
or simply 0.5
instead of (1 / 2)
.
Occassionally, you will want to explicitly perform a narrowing conversion. For example, suppose you wanted to compute the number of hairs on a human head (see Day 02):
public class HairsOnHead { public static void main(String[] args) { int diameter; diameter = 17; double fractionCovered = 0.5; double areaCovered = fractionCovered * 3.1415 * diameter * diameter; int linearDensity = 15; int areaDensity = linearDensity * linearDensity; double numberOfHairs = areaCovered * areaDensity; System.out.println(numberOfHairs); } }
The number of hairs should be an integer value. A cast can be used to round the value down towards zero (truncate the decimal part):
public class IntHairsOnHead { public static void main(String[] args) { int diameter; diameter = 17; double fractionCovered = 0.5; double areaCovered = fractionCovered * 3.1415 * diameter * diameter; int linearDensity = 15; int areaDensity = linearDensity * linearDensity; int numberOfHairs = (int) (areaCovered * areaDensity); System.out.println(numberOfHairs); } }