Floating-Point Numbers (IEEE 754)

Fixed-point versus floating-point, the IEEE 754 single and double formats, biased exponents, normalised and denormalised numbers, special values, conversions in both directions, and precision.

Integers cannot hold 0.375 or 6.02 × 10²³. Fixed-point numbers put the binary point at a fixed position (for example 8 integer bits and 8 fraction bits), which is simple but has a small range. Floating-point numbers store a significand and an exponent, like scientific notation, trading exact spacing for a huge range.

The IEEE 754 formats

A floating-point number is (−1)S × 1.M × 2E − bias:

FormatSign SExponent EMantissa M (fraction)BiasPrecision
Single (32-bit, float)1 bit8 bits23 bits127about 7 decimal digits
Double (64-bit, double)1 bit11 bits52 bits1023about 16 decimal digits
  • The exponent is stored biased (E = true exponent + bias), so it is always non-negative and floats compare like integers. For a k-bit exponent the bias is 2k−1 − 1.
  • A normalised significand always starts with 1, so that 1 is not stored (the hidden bit), giving one free bit of precision.

Converting a decimal number to IEEE single

Example: 5.75.

  1. Binary: 5.75 = 101.11₂.
  2. Normalise: 101.11 = 1.0111 × 2².
  3. Sign 0; exponent 2 + 127 = 129 = 1000 0001; mantissa 0111 followed by 19 zeros.
  4. Bits: 0 10000001 01110000000000000000000 = 0x40B80000.

Example: −0.15625 = −0.00101₂ = −1.01 × 2⁻³. Sign 1, exponent −3 + 127 = 124 = 0111 1100, mantissa 01000…: 0xBE200000.

Going back: 0xC1480000 = 1 10000010 1001000…: sign −, exponent 130 − 127 = 3, significand 1.1001₂ = 1.5625, value −1.5625 × 8 = −12.5.

Python's struct module shows the real bit patterns:

import struct

def float_bits(x):
    raw = struct.unpack(">I", struct.pack(">f", x))[0]
    b = format(raw, "032b")
    return f"{b[0]} {b[1:9]} {b[9:]}", hex(raw)

def decode(hex_string):
    raw = int(hex_string, 16)
    s, e, m = raw >> 31, (raw >> 23) & 0xFF, raw & 0x7FFFFF
    value = (-1) ** s * (1 + m / 2 ** 23) * 2.0 ** (e - 127)
    return s, e, value

for x in [5.75, -0.15625, 0.1]:
    bits, h = float_bits(x)
    print(f"{x:9}: {bits}  {h}")
print("0xC1480000 -> sign, exponent, value:", decode("C1480000"))
print("0.1 as stored:", f"{struct.unpack('>f', struct.pack('>f', 0.1))[0]:.12f}")
Output
     5.75: 0 10000001 01110000000000000000000  0x40b80000
 -0.15625: 1 01111100 01000000000000000000000  0xbe200000
      0.1: 0 01111011 10011001100110011001101  0x3dcccccd
0xC1480000 -> sign, exponent, value: (1, 130, -12.5)
0.1 as stored: 0.100000001490

0.1 has an infinite binary expansion, so the stored value is only the nearest representable float: 0.100000001490116…. This is why 0.1 + 0.2 == 0.3 is false in most programming languages.

Special values

The exponent fields of all 0s and all 1s are reserved:

Exponent EMantissa MMeaning
00±0 (two zeros)
0≠ 0Denormalised: (−1)S × 0.M × 2−126 (no hidden 1)
1 to 254anyNormalised: (−1)S × 1.M × 2E − 127
2550±∞ (overflow, or 1/0)
255≠ 0NaN (not a number: 0/0, √−1)

Denormalised numbers fill the gap between 0 and the smallest normalised number, giving gradual underflow.

Single precisionValue
Largest finite(2 − 2⁻²³) × 2¹²⁷ ≈ 3.4 × 10³⁸
Smallest positive normalised2⁻¹²⁶ ≈ 1.18 × 10⁻³⁸
Smallest positive denormalised2⁻¹⁴⁹ ≈ 1.4 × 10⁻⁴⁵

Precision and rounding

Floats are not evenly spaced: between consecutive powers of 2 there are always 2²³ single-precision values, so the gap between neighbours doubles with every power of 2. Above 2²⁴ = 16,777,216, a float can no longer represent every integer. Results are rounded (by default to the nearest value, ties to even), so floating-point addition is not associative: (a + b) + c may differ from a + (b + c).

Practice questions

GATE-styleMCQ · one correct answer

What is the IEEE 754 single-precision bit pattern of −2.5?

import struct
print(hex(struct.unpack(">I", struct.pack(">f", -2.5))[0]).upper().replace("0X", "0x"))
Show answer and explanationAnswer: B

−2.5 = −10.1₂ = −1.01 × 2¹. Sign 1, exponent 1 + 127 = 128 = 1000 0000, mantissa 0100…: 1100 0000 0010 0000 … = 0xC0200000.

GATE-styleNAT · numerical answer

What decimal value does the IEEE single-precision pattern 0x3F400000 represent?

Show answer and explanationAnswer: 0.75

0x3F400000 = 0 01111110 1000…: exponent 126 − 127 = −1, significand 1.1₂ = 1.5, value 1.5 × 2⁻¹ = 0.75.

GATE-styleNAT · numerical answer

A floating-point format has a 6-bit exponent stored with the usual IEEE-style bias. What is the bias?

Show answer and explanationAnswer: 31

For a k-bit exponent the bias is 2k−1 − 1 = 2⁵ − 1 = 31.

GATE-styleMSQ · one or more correct answers

Which statements about IEEE 754 single precision are true? (Select all that apply.)

Show answer and explanationAnswer: A, B, D

The hidden bit gives 24 bits of precision from 23 stored bits. All-1s exponents mark infinity (mantissa 0) and NaN (mantissa non-zero). Only integers up to 2²⁴ are all exact. +0 and −0 differ in the sign bit.