Try the Number Base Converter

IEEE 754 Floating-Point: Why 0.1 + 0.2 ≠ 0.3 and What to Use for Financial Calculations

0.1 + 0.2 ≠ 0.3 in nearly every programming language — because 0.1 can't be represented exactly in binary. Here's the IEEE 754 bit structure (sign, exponent, mantissa), special values (NaN, Infinity, -0), why integers above 2^53 lose precision in JavaScript, and why financial code should never use floats.

By sadiqbd · June 15, 2026

Share:
IEEE 754 Floating-Point: Why 0.1 + 0.2 ≠ 0.3 and What to Use for Financial Calculations

0.1 + 0.2 does not equal 0.3 in most programming languages — and understanding why prevents a class of financial calculation bugs

0.1 + 0.2 === 0.3  // false
0.1 + 0.2          // 0.30000000000000004

This is not a bug in JavaScript. Python, Ruby, Java, C, and virtually every language that uses IEEE 754 floating-point arithmetic produces the same result. The issue is fundamental: decimal fractions like 0.1 cannot be represented exactly in binary floating-point, just as the fraction 1/3 cannot be represented exactly in decimal (0.333... repeating). Understanding how IEEE 754 works explains this behaviour and every other floating-point surprise developers encounter.


How IEEE 754 double-precision works

Every 64-bit double-precision float is stored as three components:

[sign: 1 bit][exponent: 11 bits][mantissa: 52 bits]

Sign: 0 = positive, 1 = negative.

Exponent: stores a biased exponent. The actual exponent = stored value − 1023. This allows both very large and very small numbers in the same format.

Mantissa (significand): stores the fractional binary digits. With an implicit leading 1 bit, this gives 53 bits of precision total.

The precision limit: 53 bits of binary significand = approximately 15–17 significant decimal digits. The number 0.1 in binary is a repeating fraction:

0.1 (decimal) = 0.0001100110011001100110011... (binary, repeating)

The mantissa can only store 52 explicit bits of this repeating pattern. The stored value is the closest representable 64-bit float to 0.1 — which is:

0.1000000000000000055511151231257827021181583404541015625

Not exactly 0.1. When 0.1 and 0.2 are added, the rounding errors from both representations accumulate, producing a result that is off by about 5.5 × 10⁻¹⁷.


Dissecting a float: hex representation

The hex representation of a float directly shows its IEEE 754 bit pattern. This is where the Number Base Converter becomes genuinely useful for debugging:

0.1 as a 64-bit double:

Binary pattern:

0 01111111011 1001100110011001100110011001100110011001100110011010
↑ ↑           ↑
│ │           └─ mantissa (52 bits)
│ └─────────────── exponent (11 bits): 01111111011 = 1019, actual = 1019 - 1023 = -4
└─────────────────── sign: 0 (positive)

Hex: 3FB999999999999A

Reading this: sign = positive, exponent = -4 (so value is in range 2⁻⁴ = 0.0625 to 2⁻³ = 0.125), and the mantissa fills in the decimal value within that range.


Special IEEE 754 values

IEEE 754 reserves certain bit patterns for special values:

NaN (Not a Number):

  • Result of: 0/0, Math.sqrt(-1), Infinity - Infinity, NaN + anything
  • NaN is not equal to itself: NaN === NaN is false (the only value with this property)
  • Check for NaN: Number.isNaN(value) or isNaN(value) in JavaScript; math.isnan(value) in Python

Infinity:

  • 1/0 = Infinity, -1/0 = -Infinity in JavaScript (not an error)
  • Infinity + 1 = Infinity, Infinity - Infinity = NaN

Negative zero (-0):

  • IEEE 754 has distinct +0 and -0
  • 0 === -0 is true, but 1/0 !== 1/-0 (Infinity vs -Infinity)
  • Object.is(0, -0) returns false; useful to detect -0 specifically
1/0        // Infinity
1/-0       // -Infinity
0/-0       // NaN
NaN === NaN  // false
Number.isNaN(NaN)  // true
Object.is(0, -0)   // false
0 === -0           // true

Float precision by number range

IEEE 754 doubles have variable absolute precision depending on the magnitude of the number. Precision is measured in units in the last place (ULP):

  • Numbers near 1: precision ≈ 2.2 × 10⁻¹⁶
  • Numbers near 1,000: precision ≈ 2.3 × 10⁻¹³
  • Numbers near 1,000,000: precision ≈ 1.9 × 10⁻¹⁰

The integer precision limit:

Number.MAX_SAFE_INTEGER  // 9007199254740991 (2^53 - 1)
9007199254740992 === 9007199254740993  // true — cannot distinguish!

Beyond 2⁵³, consecutive integers are no longer distinguishable. This is why JavaScript APIs from Twitter, Discord, and others return large integer IDs as strings.


Why financial calculations should never use floats

The problem with floats in money:

price = 19.99
quantity = 3
total = price * quantity  # 59.97? No:
print(total)  # 59.97000000000000397903932...

A retail system that accumulates these errors across thousands of transactions will show incorrect totals in reports.

The correct approach for money calculations:

Option 1: Integer arithmetic in the smallest unit Store prices in pence/cents (integers). £19.99 = 1999 pence. All arithmetic is integer arithmetic. Convert to decimal only for display.

price_pence = 1999
quantity = 3
total_pence = price_pence * quantity  # 5997 (exact)
total_display = total_pence / 100     # 59.97 (display only)

Option 2: Decimal type Use a fixed-point or arbitrary-precision decimal library:

from decimal import Decimal, ROUND_HALF_UP

price = Decimal("19.99")
quantity = Decimal("3")
total = price * quantity  # Decimal('59.97') — exact
// JavaScript: use decimal.js or similar
const Decimal = require('decimal.js');
const total = new Decimal('19.99').times(3);  // '59.97' — exact

Option 3: Round aggressively before display For simple cases, round to the relevant decimal places before comparison or display. This doesn't fix the underlying representation but masks errors that are smaller than the rounding increment.


How to use the Number Base Converter on sadiqbd.com

  1. Enter a decimal number — see the binary, octal, and hexadecimal representation
  2. Debug float bit patterns — convert hex representations of floats (from memory dumps, debugging tools) to understand the underlying value
  3. Cross-check integer boundaries — verify that values near 2⁵³ are representable as safe integers
  4. Convert between bases — for any purpose from colour codes (#FF6B6B = RGB 255, 107, 107) to Unix permissions (chmod 755 = 111 101 101 binary)

Frequently Asked Questions

Why does 0.1 + 0.2 give different results in different languages? It doesn't — almost all languages using IEEE 754 doubles produce 0.30000000000000004. Some languages (Python, Ruby) display a rounded representation that looks like 0.3 but the underlying value is 0.30000000000000004. Python 3: repr(0.1 + 0.2) shows 0.30000000000000004; print(0.1 + 0.2) may show 0.30000000000000004 or 0.3 depending on version.

What is float32 vs float64 and when does it matter? Float32 (single precision) uses 32 bits: 1 sign, 8 exponent, 23 mantissa. Precision ≈ 7 significant decimal digits. Float64 (double precision, the JavaScript number type): 64 bits, ≈ 15–17 digits. Float32 is used in GPU computation, machine learning (where memory bandwidth is the bottleneck and slight precision loss is acceptable), and embedded systems. For most backend development, float64 is standard.

Is the Number Base Converter free? Yes — completely free, no sign-up required.

Try the Number Base Converter free at sadiqbd.com — convert any number between binary, octal, decimal, and hexadecimal instantly.

Share:
Try the related tool:
Open Number Base Converter

More Number Base Converter articles