skip to content

How do floating-point literals and scientific notation work in Java, including special values and the difference between float and double precision?

level: seniorimportance: should knowfreq 35%

answer

  1. Decimal point or exponent makes it floating-point
  2. Default double; f = float; e/E = decimal exponent
  3. 1.5e3 = 1500.0; 0x1.8p1 = 3.0 (p = binary exponent)
  4. No literal for Infinity/NaN -> use Double.* constants
  5. 0.1+0.2 != 0.3; never == on floats; no money in double

basics

~20 s

Floating-point literals have a decimal point or an exponent: 3.14, 1.5e3 (means 1500), .5, 2f. Without a suffix they are double (64-bit, more precise); with f they are float (32-bit, less precise). There is no literal for infinity or NaN — those come from constants.

solid answer

~40 s

A floating-point literal needs either a decimal point or an exponent (or an f/d suffix). Forms include 3.14, .5, 5., 1.5e3 (scientific notation = 1.5 x 10^3 = 1500.0), and 2e-4. By default they are double (64-bit, ~15-16 significant decimal digits); the f suffix makes a float (32-bit, ~6-7 digits), and d is an explicit double. There are also hexadecimal floating-point literals like 0x1.8p1 (the p marks a binary exponent), mainly for exact bit-level constants. Crucially, there is no literal syntax for infinity or NaN; you write Double.POSITIVE_INFINITY, Double.NaN, etc. Because these are IEEE-754 binary fractions, many decimal values like 0.1 are not represented exactly, so 0.1 + 0.2 != 0.3, and you should never use == on floating-point or use them for money (use BigDecimal).

code

java · 15 lines
java
double a = 3.14;       // double (default)
double b = 1.5e3;      // scientific: 1.5 x 10^3 = 1500.0
double c = 2e-4;       // 0.0002
double half = .5;      // leading zero optional
float  f = 3.14f;      // f suffix required for float
double hex = 0x1.8p1;  // hex float: 1.5 x 2^1 = 3.0

// No literal for these:
double inf = Double.POSITIVE_INFINITY;
double nan = Double.NaN;
System.out.println(nan == nan);        // false! use Double.isNaN(nan)

// Inexactness trap:
System.out.println(0.1 + 0.2);         // 0.30000000000000004
System.out.println(0.1 + 0.2 == 0.3);  // false -- never compare with ==

go deeper

for a junior

Recognise that 3.14 is a double, that f makes a float, and that 1.5e3 means 1500.

for a middle

Explain default double vs float precision, all the literal forms including scientific notation, and that you compare floats with a tolerance, not ==.

for a senior

Explain IEEE-754 binary representation and inexactness, the absence of infinity/NaN literals, NaN's self-inequality, hex float literals, and the money/BigDecimal rule.

for a principal

Discuss numeric-stability and rounding-mode trade-offs, when float is justified (memory/SIMD/GPU), BigDecimal construction pitfalls (String vs double ctor), and codebase conventions/lints banning == on floats and double for currency.

## What makes a literal floating-point A literal is **floating-point** (a number with a fractional part) rather than an integer if it has at least one of: a **decimal point**, an **exponent**, or a **floating-point suffix** (`f`/`F`/`d`/`D`). Examples: `3.14`, `0.0`, `.5` (leading zero optional), `5.` (trailing digits optional), `2f`, `1e9`. ## Scientific (exponent) notation **Scientific notation** writes a number as a mantissa times a power of ten, using `e` or `E`: `1.5e3` means 1.5 x 10^3 = 1500.0, and `2e-4` means 2 x 10^-4 = 0.0002. The presence of `e` alone makes the literal floating-point even without a decimal point (`1e9` is a `double`). This is handy for very large or very small constants (speeds, tolerances, physical constants). ## Default type and suffixes - **No suffix -> `double`** (64-bit IEEE-754, about **15-16** significant decimal digits of precision). - **`f`/`F` -> `float`** (32-bit IEEE-754, only about **6-7** significant digits). Required to assign a fractional constant to a `float` variable (`float r = 3.14f;`), since `double`->`float` is a narrowing conversion Java won't do implicitly. - **`d`/`D` -> `double`** explicitly (rarely needed; documents intent or forces an integer-looking literal to be double, e.g. `1d`). ## Hexadecimal floating-point literals (advanced) Java also supports **hex floating-point literals**, which use a `0x` prefix, hex digits, and a **`p`** (binary) exponent: `0x1.8p1` means (1 + 8/16) x 2^1 = 1.5 x 2 = 3.0. These let you specify a floating value by its exact binary representation, useful in numerics and for reproducing precise constants. The `p` (power-of-two) exponent is mandatory in this form. ## There is no literal for infinity or NaN IEEE-754 defines special values — **positive/negative infinity** and **NaN** ("Not a Number", the result of `0.0/0.0` or `sqrt(-1)`). Java has **no literal** for them. You obtain them from constants: `Double.POSITIVE_INFINITY`, `Double.NEGATIVE_INFINITY`, `Double.NaN` (and the `Float` equivalents). NaN is special: `NaN != NaN` is `true`, so you must test with `Double.isNaN(x)`. ## Precision and the inexactness trap Both `float` and `double` store numbers as **binary fractions** (sums of powers of two). Many ordinary decimal values cannot be represented exactly in binary — `0.1`, `0.2`, `0.3` are all approximations. So: ```java System.out.println(0.1 + 0.2); // 0.30000000000000004, not 0.3 System.out.println(0.1 + 0.2 == 0.3); // false! ``` Key rules that follow: - **Never compare floating-point with `==`.** Compare within a small tolerance (epsilon): `Math.abs(a - b) < 1e-9`. - **Never use float/double for money** or anything needing exact decimal arithmetic; use `BigDecimal` (constructed from a String, not a double, to avoid carrying the binary error in). - `float` loses precision fast (~7 digits), so prefer `double` unless memory/SIMD demands `float`. ## Summary Floating-point literals: decimal point or exponent, default `double`, `f` for `float`, `e`/`E` for decimal exponent, `0x...p...` for hex form. No literal for infinity/NaN. Treat all floating-point values as approximate: no `==`, no money.

  • Why is 0.1 + 0.2 == 0.3 false in Java?
    double uses IEEE-754 binary floating point; 0.1, 0.2, and 0.3 have no exact binary representation, so they are stored as tiny approximations. The sum 0.1 + 0.2 rounds to 0.30000000000000004, which differs from the stored 0.3. Compare with a tolerance, or use BigDecimal for exact decimal arithmetic.
  • How do you write infinity or NaN as a literal?
    You cannot — there is no literal syntax for them. Use the constants Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN (and Float equivalents). Note NaN != NaN, so test with Double.isNaN().

saying these in an interview costs you the question

  • Comparing floating-point values with ==
  • Using float/double for currency instead of BigDecimal
  • Thinking there is a literal like Infinity or NaN
  • Assuming 0.1 is stored exactly
  • Forgetting the f suffix when assigning to a float

context