All subjects · Computer Science · WCP04 Advanced Practical Programming and Problem-solving

Floating-point data and the decimal module

Edexcel IAL Computer Science · WCP04 Advanced Practical Programming and Problem-solving

Floats are stored in binary, so most decimal fractions are approximations. For money and other exact decimal work, Python's `decimal` module gives exact results and full control of rounding.

Practise this in the app: instant marking and a fix for every mistake. Free, no email needed to start.

Practise this lesson

Why floats go wrong

A float uses binary fractions (halves, quarters, eighths and so on). 0.1 has no exact binary form, just as one third has no exact decimal form, so tiny errors appear and == comparisons can fail. A Decimal stores the decimal digits exactly.

from decimal import Decimal
print(0.1 + 0.2, 0.1 + 0.2 == 0.3)
print(Decimal("0.1") + Decimal("0.2"), Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))
print(Decimal(0.1))

Output:

0.30000000000000004 False
0.3 True
0.1000000000000000055511151231257827021181583404541015625

Always create a Decimal from a string, such as Decimal("0.1"). Decimal(0.1) copies the float's error exactly, as the last line shows.

Rounding and precision

from decimal import Decimal, getcontext, ROUND_HALF_UP
cost = Decimal("2.665")
print(cost.quantize(Decimal("0.01")))
print(cost.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))
getcontext().prec = 6
print(Decimal(1) / Decimal(7))

Output:

2.66
2.67
0.142857

Type conversion and exceptions

from decimal import Decimal, InvalidOperation
for text in ["12.50", "twelve"]:
    try:
        print(Decimal(text) * 2)
    except InvalidOperation:
        print("not a number:", text)
try:
    print(Decimal("1.5") + 0.5)
except TypeError:
    print("cannot mix Decimal and float")
try:
    print(Decimal(1) / Decimal(0))
except ZeroDivisionError:
    print("division by zero")

Output:

25.00
not a number: twelve
cannot mix Decimal and float
division by zero

Comparing values

import math
from decimal import Decimal
a, b = Decimal("2.5"), Decimal("2.50")
print(a == b, a.compare(b), Decimal("3").compare(a), Decimal("-4.2").copy_abs())
print(max(Decimal("1.10"), Decimal("1.9")), Decimal("2").sqrt().quantize(Decimal("0.001")))
print(math.isclose(0.1 + 0.2, 0.3))

Output:

True 0 1 4.2
1.9 1.414
True

What the PLS lists, and how Python writes it

The PLS decimal list is: Decimal(value), getcontext() and getcontext().prec, sqrt(), compare, copy_abs(), min(), max(), logical_and(), logical_or() and logical_xor(). Points to watch:

from decimal import Decimal, getcontext
x, y = Decimal("3.2"), Decimal("5")
print(x.compare(y), getcontext().compare(y, x), round(Decimal("2.665"), 2), round(Decimal("2.675"), 2))
print(Decimal("1100").logical_and(Decimal("1010")), Decimal("1100").logical_or(Decimal("1010")), Decimal("1100").logical_xor(Decimal("1010")))

Output:

-1 1 2.66 2.68
1000 1110 110

Worked example

A shop sells 3 items at 19.99 and adds 20% VAT. Calculate the total to the nearest penny with halves rounded up.

  1. ```python from decimal import Decimal, ROUND_HALF_UP PRICE = Decimal("19.99") VAT = Decimal("0.20") total = PRICE * 3 * (1 + VAT) print(total) print(total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)) ```
  2. Output: ``` 71.9640 71.96 ```

Answer: Every value is created from a string, so the arithmetic is exact, and quantize applies the required rounding once, at the end.

Key idea: Floats are binary approximations; Decimal('0.1') is exact. quantize to round (default half-even, ROUND_HALF_UP on request); getcontext().prec sets significant figures; Decimal + float is a TypeError; bad strings raise InvalidOperation; compare floats with math.isclose.

Check yourself

  1. What does this program print? ```python print(0.1 + 0.2 == 0.3, round(0.1 + 0.2, 10) == 0.3) ```
    Show answer

    False True. 0.1 + 0.2 is stored as 0.30000000000000004, so the first test fails; rounding first removes the tiny error. Output: `False True`

  2. What does this program print? ```python from decimal import Decimal, ROUND_HALF_UP x = Decimal("4.125") print(x.quantize(Decimal("0.01")), x.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)) ```
    Show answer

    4.12 4.13. 4.125 is exactly half way. Half-even goes to the even digit, 4.12; half-up gives 4.13. Output: `4.12 4.13`

  3. What does this program print? ```python from decimal import Decimal, getcontext getcontext().prec = 4 print(Decimal(2) / Decimal(3)) ```
    Show answer

    0.6667. prec = 4 keeps 4 significant figures, rounding the last: `0.6667`

  4. What does this program print? ```python from decimal import Decimal print(Decimal("1.1") * 3, 1.1 * 3) ```
    Show answer

    3.3 3.3000000000000003. The Decimal result is exact, 3.3. The float result shows binary error. Output: `3.3 3.3000000000000003`

  5. What does this program print? ```python from decimal import Decimal print(Decimal("7.0").compare(Decimal("7.5"))) ```
    Show answer

    -1. 7.0 is less than 7.5, so compare returns `-1`.

  6. What happens when Python evaluates `Decimal("2.5") + 1.5`?
    • It returns Decimal('4.0')
    • It returns the float 4.0
    • It raises InvalidOperation
    • A TypeError is raised
    Show answer

    A TypeError is raised. Python refuses to mix Decimal and float implicitly, because the float may carry binary error. Write `Decimal("1.5")` instead.

  7. Which line creates the exact value 0.7 as a Decimal?
    • `Decimal(0.7)`
    • `Decimal("0.7")`
    • `Decimal(7 / 10)`
    • `float(Decimal("0.7"))`
    Show answer

    `Decimal("0.7")`. A string gives the exact decimal digits; the float 0.7 is already an approximation before Decimal sees it.

  8. A program converts user input with `Decimal(text)`. Which exception should it catch for input such as 'ten'?
    • decimal.InvalidOperation
    • ValueError only
    • ZeroDivisionError
    • KeyError
    Show answer

    decimal.InvalidOperation. Decimal raises InvalidOperation for a string that is not a number. (InvalidOperation also counts as an ArithmeticError.)

Practise with instant feedback

Sources

Last updated 8 November 2026. Spotted a mistake? Tell us.