2.4 Floating-Point Operations
Java provides two primitive data types for representing floating-point numbers (numbers with decimal points):
float
: A 32-bit single-precision floating-point number (approximately 6-7 decimal digits of precision).double
: A 64-bit double-precision floating-point number (approximately 15 decimal digits of precision).
Declaring Floating-Point Literals
float price = 19.99f; // Note the 'f' suffix for float
double pi = 3.1415926535; // 'd' suffix is optional for double
double largeNumber = 1.23e10; // Scientific notation: 1.23 × 10¹⁰
Basic Arithmetic Operations
Floating-point numbers support the same arithmetic operators as integers:
Operator | Operation | Example | Result |
---|---|---|---|
+ | Addition | 5.5 + 2.3 | 7.8 |
- | Subtraction | 10.0 - 3.7 | 6.3 |
* | Multiplication | 2.5 * 4.0 | 10.0 |
/ | Division | 15.0 / 4.0 | 3.75 |
% | Modulus | 10.5 % 3.0 | 1.5 |
Unlike integer division, floating-point division preserves the fractional part:
double result = 7.0 / 2.0; // 3.5, not 3
Special Floating-Point Values
Floating-point arithmetic can produce special values defined by the IEEE 754 standard:
Positive Infinity: Result of dividing a positive number by zero.
javadouble posInf = 5.0 / 0.0; // Infinity
Negative Infinity: Result of dividing a negative number by zero.
javadouble negInf = -5.0 / 0.0; // -Infinity
NaN (Not a Number): Result of undefined operations.
javadouble nan = 0.0 / 0.0; // NaN double nan2 = Math.sqrt(-1); // NaN
Checking Special Values
Double.isInfinite(posInf); // true
Double.isNaN(nan); // true
Precision and Rounding Errors
Floating-point numbers use binary representation, which can lead to precision issues with decimal fractions:
double result = 0.1 + 0.2; // Not exactly 0.3!
System.out.println(result); // 0.30000000000000004
This occurs because numbers like 0.1 cannot be represented exactly in binary floating-point format, similar to how 1/3 cannot be represented exactly in decimal (0.333...).
Type Conversion and Promotion
When mixing different numeric types in operations:
- If either operand is
double
, the result isdouble
. - Otherwise, if either operand is
float
, the result isfloat
. - Otherwise, if either operand is
long
, the result islong
. - Otherwise, the result is
int
.
Examples
float f = 5.0f;
double d = 10.0;
int i = 3;
double result1 = f + d; // double
float result2 = f + i; // float (int promoted to float)
double result3 = d + i; // double (int promoted to double)
Explicit Casting
double precise = 123.456;
float approx = (float) precise; // Explicit narrowing cast
Comparison and Equality Testing
Due to precision issues, avoid using ==
for exact floating-point comparisons:
// Problematic:
if (0.1 + 0.2 == 0.3) { // This will be false!
// Code may not execute as expected
}
// Better approach: use tolerance-based comparison
final double EPSILON = 1e-10; // Small tolerance value
double sum = 0.1 + 0.2;
if (Math.abs(sum - 0.3) < EPSILON) {
// Considered equal for practical purposes
}
For comparisons, use:
Math.abs(a - b) < tolerance
for equality.- Standard
<
,<=
,>
,>=
operators for ordering.
Mathematical Functions
The Math
class provides common mathematical operations:
double x = 25.0;
double y = 3.0;
double sqrt = Math.sqrt(x); // 5.0 - square root
double power = Math.pow(x, y); // 15625.0 - x raised to power y
double abs = Math.abs(-7.5); // 7.5 - absolute value
double round = Math.round(3.7); // 4.0 - nearest integer
double ceil = Math.ceil(3.2); // 4.0 - round up
double floor = Math.floor(3.9); // 3.0 - round down
// Trigonometric functions (work in radians)
double sin = Math.sin(Math.PI / 2); // 1.0
double cos = Math.cos(Math.PI); // -1.0
Best Practices and Common Pitfalls
Best Practices
- Prefer
double
overfloat
for most applications due to better precision. - Avoid
==
for exact comparisons - use tolerance-based approaches. - Be aware of cumulative errors in repeated calculations.
- Use
BigDecimal
for financial calculations where exact decimal representation is required. - Handle special values (Infinity, NaN) appropriately in your code.
- Consider performance implications - floating-point operations are generally slower than integer operations.
Common Mistakes
// Mistake 1: Forgetting the 'f' suffix
float wrong = 3.14; // Error: requires cast or 'f' suffix
float correct = 3.14f; // Correct
// Mistake 2: Integer division in floating-point context
double result = 5 / 2; // 2.0, not 2.5 - both operands are integers
double correct = 5.0 / 2; // 2.5 - at least one operand is floating-point
// Mistake 3: Comparing NaN with ==
double nan = 0.0 / 0.0;
if (nan == Double.NaN) { // Always false!
// This will never execute
}
if (Double.isNaN(nan)) { // Correct way to check
// This will execute
}
Example Code Snippet
public class FloatOperations {
public static void main(String[] args) {
// Basic operations
double a = 15.75;
double b = 4.25;
System.out.println("a + b = " + (a + b)); // 20.0
System.out.println("a - b = " + (a - b)); // 11.5
System.out.println("a * b = " + (a * b)); // 66.9375
System.out.println("a / b = " + (a / b)); // 3.7058823529411766
System.out.println("a % b = " + (a % b)); // 3.0
// Precision example
double preciseSum = 0.1 + 0.2;
System.out.println("0.1 + 0.2 = " + preciseSum); // 0.30000000000000004
// Proper comparison with tolerance
final double TOLERANCE = 1e-10;
if (Math.abs(preciseSum - 0.3) < TOLERANCE) {
System.out.println("0.1 + 0.2 is effectively 0.3");
}
// Math class functions
double number = 16.0;
System.out.println("Square root: " + Math.sqrt(number)); // 4.0
System.out.println("Square: " + Math.pow(number, 2)); // 256.0
System.out.println("Rounded: " + Math.round(3.6)); // 4.0
// Special values
double infinity = 5.0 / 0.0;
double nan = 0.0 / 0.0;
System.out.println("Infinity: " + infinity); // Infinity
System.out.println("NaN: " + nan); // NaN
System.out.println("Is infinity? " + Double.isInfinite(infinity)); // true
System.out.println("Is NaN? " + Double.isNaN(nan)); // true
}
}