1. Rounding real numbers
As we have already discussed, when a real number is assigned to an int
variable, it is always rounded down to the nearest smaller integer — the fractional part is simply discarded.
But it's easy to imagine a situation when a fractional number needs to be rounded to the nearest integer in either direction or even rounded up. What do you do in this case?
For this and for many similar situations, Java has the Math
class, which has the round()
, ceil()
, and floor()
methods.
Math.round()
method
The Math.round()
method rounds a number to the nearest integer:
long x = Math.round(real_number)
But there's another nuance here: this method returns a long
integer (not an int
). Because real numbers can be very large, Java's creators decided to use Java's largest available integer type: long
.
Accordingly, if a programmer wants to assign the result to an int
variable, then she must explicitly indicate to the compiler that she accepts the possible loss of data (in the event that the resulting number does not fit into an int
type).
int x = (int) Math.round(real_number)
Examples:
Statement | Result |
---|---|
|
|
|
|
|
|
Math.ceil()
method
The Math.ceil()
method rounds a number up to an integer. Here are examples:
Statement | Result |
---|---|
|
|
|
|
|
|
Math.floor()
method
The Math.floor()
method rounds a number down to an integer. Here are examples:
Statement | Result |
---|---|
|
|
|
|
|
|
Of course, when rounding a number down to an integer, it's easier to simply use a type cast operator: (int)
Statement | Result |
---|---|
|
|
If you find it difficult to remember these names, a short English lesson will help:
Math
means mathematicsRound
means roundCeiling
means ceilingFloor
means floor
2. How floating-point numbers are structured
The double
type can store values in the range from -1.7*10308
to +1.7*10308
. This huge range of values (compared with the int
type) is explained by the fact that the double
type (as well as float
) has a completely different internal structure than integer types. Internally, the double
type encodes its value as two numbers: the first is called the mantissa, and the second is called the exponent.
Let's say we have the number 123456789
and store it a double
variable. When we do, the number is converted to 1.23456789*108
, and internally the double
type stores two numbers — 23456789
and 8
. The significand ("significant part of the number" or mantissa) is highlighted in red, while the exponent is highlighted in blue.
This approach makes it possible to store both very large numbers and very small ones. But because the number's representation is limited to 8 bytes (64 bits) and some of the bits are used to store the exponent (as well as the sign of the mantissa and the sign of the exponent), the maximum digits available to represent the mantissa is 15.
This is a very simplified description of how real numbers are structured.
3. Loss of precision when working with real numbers
When working with real numbers, always keep in mind that real numbers are not exact. There may always be rounding errors and conversion errors when converting from decimal to binary. Additionally, the most common source of error is loss of precision when adding/subtracting numbers on radically different scales.
This last fact is a little mind-blowing for novice programmers.
If we subtract 1/109
from 109
, we get 109
.
Subtracting numbers on radically different scales | Explanation |
---|---|
|
The second number is extremely small, which will cause its significand (highlighted in gray) is be ignored. The 15 significant digits are highlighted in orange. |
What can we say, programming isn't the same as mathematics.
4. Pitfall when comparing real numbers
Another danger lies in wait for programmers when they compare real numbers. It arises when working with real numbers, because round-off errors can accumulate. The result is that there are situations when real numbers are expected to be equal, but they are not. Or vice versa: the numbers are expected to be different, but they are equal.
Example:
Statement | Explanation |
---|---|
|
The value of the variable a will be 1000000000.0 The value of the variable c will be 1000000000.0 (the number in the b variable is excessively small)
|
In the above example, a
and c
should not be equal, but they are.
Or let's take another example:
Statement | Explanation |
---|---|
|
The value of the variable a will be 1.0 The value of the variable b will be 1.0
|
5. An interesting fact about strictfp
Java has a special strictfp
keyword (strict floating point), which is not found in other programming languages. And do you know why you need it? It worsens the accuracy of operations with floating-point numbers. Here's the story of how it came to be:
GO TO FULL VERSION