INTRODUCTION
In today's free Java lesson, we will analyze floating-point data types, what to watch out for when performing arithmetic operations with floating-point numbers, but most importantly, how we can define the order of execution of arithmetic operations to obtain the correct result.
FLOATING-POINT TYPES
Apart from integers, we can define a Java variable to accept decimal numbers as well. The way to assign decimal numbers to a variable is by declaring it as float
or double
. The only difference between these two data types is the range of numbers they can cover since a float
number is represented by 32 bits, while a double
is represented by 64 bits.
In the previous free Java lesson, we mentioned that Java's default behavior with integral numbers is to try to convert them all to int
, unless we add an L
at the end of the number and declare the data type as long
.
For floating-point numbers, the default behavior in Java is the double
data type. If we want to define a number as float
, in addition to declaring the float
data type before the variable name, we must also add an F
at the end of the number for it to be recognized as a float
.
In general, the double
data type is the one we use most often, as all mathematical libraries in Java use and return double
numbers by default. Let’s look at a simple example before moving on to specific points that we need to be especially careful about when programming with double
variables.
App.java
Output
The above program is one we have seen before when analyzing integral data types. However, by using double
, we gain greater precision for decimal places, something we did not have with integers. As you may recall from the explanation in the previous section, this code sets the initial price of a product. After calculating the discount, we subtract it from the original price to display the final price of the product to the user.
In this particular program, there is nothing special to watch out for because the arithmetic operations were simple. Now, let's look at another program where, despite the logic being straightforward, you might get an incorrect result if you don’t fully understand how Java works. This program stores the results of three different tests and calculates the average score.
App.java
Output
The program runs fine without Java complaining about any syntax errors, but the result of the division is incorrect. The correct result should be 90.666
, not 90.0
. What happened here? To fully understand how we ended up with this incorrect result, let's break down the line that calculates the average step by step.
To find the average of a group of numbers, we first need to add them together. At this point in the code, we are doing exactly that—adding the three test scores.
Now, your next question might be: why is the parenthesis necessary?
In Java, arithmetic operations are executed based on operator precedence. Here's a breakdown of how it works:
Operator Precedence in Java
- Multiplication (
*
), Division (/
), and Remainder (%
) are executed first. - If we have multiple operators of the same precedence (for example, multiplication and division), they are evaluated from left to right (also known as left-associative).
- Addition (
+
) and Subtraction (-
) are evaluated last. - Similarly, if there are multiple addition or subtraction operators in the same expression, these are also evaluated from left to right.
If we want to control the order of operations in Java and override the default precedence set by arithmetic operators, we can use parentheses (( )
) around the operations we want to execute first.
Why Parentheses Matter?
- Parentheses have the highest precedence of all arithmetic operators in Java.
- Any operation inside parentheses will be executed first, before other operations, regardless of their precedence.
Going back to our simple example, we see that there is an addition of three variables and a division. If we don't place the three variables inside parentheses, Java will follow the default operator precedence and perform the division first. If you don't use parentheses, the division will occur before the addition, which can lead to incorrect results.
At this point, Java has added the variables, and the result is the int number 272. This number is divided by the int number 3. Division between two int numbers cannot give us the exact result. Therefore, the result is the int number 90.
The result of the addition will be assigned as a value to the double
variable average
. However, an int
number (32-bits) is smaller than a double
(64-bits). At this point, Java performs automatic casting, converting the int
number 90
into the double
number 90.0
so that the data types are compatible. After the casting is done, the value is then assigned to the average
variable.
Now that we understand exactly what happened and why we got the wrong result, the next question is: how can we fix our code?
The answer is easy, because by understanding the theory (which we just analyzed above), we realized that we need to intervene in the code before the division takes place.Here, we could offer two possible solutions.
The first solution is to divide by the double
number 3.0
instead of the int
number 3
. By seeing that Java is going to perform division between an int
and a double
, it will automatically convert the int
to double
and then perform the division.
The second approach would be to cast the result of the addition to double
before performing the division. This ensures that the division takes place using floating-point arithmetic, even though the sum might be initially calculated with integer values.
Let's go ahead and implement the second approach in the code and see if we get the desired result. Additionally, we can print the largest (Double.MAX_VALUE
) and smallest (Double.MIN_VALUE
) values that a double
can hold, which is a useful check to understand the range of double
values in Java.
App.java
Output
Before we close today's free Java lesson, let's look at one of the most well-known examples that demonstrates the usefulness of floating-point variables: the conversion from Celsius to Fahrenheit.
This is a great example because it shows how floating-point variables can help maintain precision in calculations that involve real-world measurements.
App.java
Output
As you observed in today's lesson, Java performs widening conversions automatically because it is confident that the final result won't lose precision. The following table may help you better understand the conditions under which this automatic conversion takes place.
0 Comments
What do you think about Ground of Code?