Python float Lab

Lab: The Floating-Point Trap

Try running this in your Python terminal to see the logic gap:

  1. Step 1: Calculate 0.1 + 0.2.
  2. Step 2: Compare it to 0.3.
  3. Step 3: Use the decimal module to see the “true” value.

Python

import decimal

# The Trap
print(0.1 + 0.2 == 0.3) # Result: False

# The Reality
print(decimal.Decimal(0.1)) 
# You will see: 0.1000000000000000055511151231257827021181583404541015625


Lab: Basics & Type Conversion

Task:

Calculate the total and print its type.

Create a variable price with the value 19.99.

Convert the integer 5 to a float and store it in quantity.

import math

price = 19.99
quantity = float(5)
total = price * quantity
print(f"Total: {total}, Type: {type(total)}") 
# Output: Total: 99.95, Type: <class 'float'>


Lab: Precision & Rounding

Use a built-in function to round result to 2 decimal places.

Calculate $0.1 + 0.2$ and store it in result.

Print result. (Note: Observe if it is exactly 0.3).

import math

result = 0.1 + 0.2
print(f"Raw result: {result}") 
# Output: 0.30000000000000004 (Due to binary floating-point representation)

rounded_result = round(result, 2)
print(f"Rounded: {rounded_result}")

Why does 0.1 + 0.2 not equal 0.3?

In Python (and most programming languages), floats are represented in binary (base-2). Just as the fraction 1/3 cannot be perfectly represented in decimal (it’s 0.3333…), the fraction 1/10 (0.1) cannot be perfectly represented in binary. This leads to the tiny “overflow” you see in the raw result.


Lab: Formatting Output

Task:

Given pi_approx = 22 / 7, print the value formatted to exactly 3 decimal places using an f-string.

import math
pi_approx = 22 / 7
print(f"Pi to 3 decimal places: {pi_approx:.3f}")


Lab: The math Module

Task:

Use math.isclose() to check if 0.1 + 0.2 is effectively equal to 0.3.

Import the math module.

Use math.floor() and math.ceil() on the number 4.7.

import math

val = 4.7
print(f"Floor: {math.floor(val)}") # 4
print(f"Ceil: {math.ceil(val)}")   # 5

# Checking equality for floats
comparison = math.isclose(0.1 + 0.2, 0.3)
print(f"Is 0.1 + 0.2 close to 0.3? {comparison}")


Lab: The Initialization

Goal: Create a script that handles user input for a grocery store.

  • Ask the user for the price of an item (e.g., 2.50).
  • Ask for the quantity (e.g., 3).
  • Calculate the total. Even if the user enters “3”, ensure the calculation treats it as a float.
  • Bonus: Print the memory address of the float variable using id().
import math

# Using float() to wrap input ensures we don't get a string error
price = float(input("Enter price: ")) # User enters 2.5
quantity = float(input("Enter quantity: ")) # User enters 3
total = price * quantity

print(f"Total: {total}") # Output: 7.5
print(f"Memory ID: {id(total)}")


Lab:

 The Precision Paradox

Goal: Demonstrate why developers never use floats for financial calculations.

  • Add 0.1 and 0.2.
  • Compare the result to 0.3 using the == operator.
  • Explain why the result is False using a comment.
import math

result = 0.1 + 0.2
print(f"0.1 + 0.2 is {result}") 
# Result: 0.30000000000000004
print(result == 0.3) # Output: False

# Fix: Use math.isclose for floating point comparison
print(math.isclose(result, 0.3)) # Output: True


Lab:

Advanced Rounding

Goal: Explore the math module.

  • Given val = 12.51, use three different methods to turn it into a whole number:
    1. math.floor()
    2. math.ceil()
    3. int() conversion.
  • Observe how they differ for the same value.
import math

val = 12.51
print(math.floor(val)) # 12 (Down to nearest integer)
print(math.ceil(val))  # 13 (Up to nearest integer)
print(int(val))        # 12 (Truncates everything after decimal)


Lab:

Scientific Notation & Large Numbers

Goal: Work with very small and very large floats.

  • Create a variable atoms equal to 6.022×1023.
  • Create a variable size equal to 0.000000005.
  • Print them and see how Python automatically formats them.
import math



### Solution 4: Scientific Notation
atoms = 6.022e23
size = 5e-9
print(atoms) # 6.022e+23
print(size)  # 5e-09


Lab:

The “Boss Level” Precision (The decimal Module)

Goal: Fix the floating-point error from Challenge 2 using Python’s decimal module.

  • Import Decimal from the decimal module.
  • Create two variables using Decimal, but initialize them with strings ("0.1" and "0.2").
  • Add them together and compare the result to Decimal("0.3").
  • Question: Why must we use strings inside Decimal() instead of raw floats?
from decimal import Decimal

### Solution 5: The Decimal Module
# Using strings ensures the decimal starts exact. 
# If you used Decimal(0.1), you'd just be wrapping an imprecise float!
d1 = Decimal("0.1")
d2 = Decimal("0.2")
d_sum = d1 + d2

print(f"Decimal Sum: {d_sum}")   # Output: 0.3
print(d_sum == Decimal("0.3"))   # Output: True


Lab:

 Infinity and “Not a Number”

  • Create a variable inf_val by converting the string "inf" to a float.
  • Create a variable nan_val by converting the string "nan" to a float.
  • Try adding 1 to inf_val. What happens?
from decimal import Decimal



### Solution 6: Infinity and NaN
inf_val = float("inf")
nan_val = float("nan")

print(inf_val + 1)      # Output: inf (Infinity plus anything is still infinity)
print(nan_val == nan_val) # Output: False 
# EXPLANATION: NaN is never equal to anything, including itself. 
# To check for NaN, use math.isnan().


Lab:

Floating Point Representation

Goal: See how Python “thinks” about a float under the hood.

  • Take the float 0.125.
  • Use the method .as_integer_ratio() on it.
  • Question: What does this method tell you about how the float is stored?
from decimal import Decimal



val = 0.125
ratio = val.as_integer_ratio()
print(ratio) # Output: (1, 8)
# EXPLANATION: 0.125 is exactly 1/8. 
# Since 8 is a power of 2, this float is perfectly precise in binary

 When to use what?

ScenarioRecommended Tool
General Science/PhysicsStandard float (Fast, handles scientific notation well).
Money / Accountingdecimal.Decimal (Slow, but 100% accurate to human decimals).
Complex Mathmath or cmath module.
Massive Data Setsnumpy.float64 (Extremely memory efficient).

Lab:

Mixed-Type Arithmetic

Goal: Understand “Coercion” (how Python promotes types).

  • Create an integer a = 5 and a float b = 2.0.
  • Divide a / 2. What is the resulting type?
  • Perform a + b. What is the resulting type?
  • Question: Why does Python choose to turn integers into floats during math instead of the other way around?
import math

### Solution 8: Type Coercion
a = 5
b = 2.0

print(type(a / 2))   # <class 'float'> (True division always returns a float)
print(type(a + b))   # <class 'float'> 

# EXPLANATION: Python "promotes" the integer to a float because floats 
# can represent a wider range of values (like decimals). 
# Converting a float to an int would lose data (truncation).


Lab:

Dealing with “Dirty” Strings

Goal: Clean up data commonly found in web scraping or CSV files.

  • You are given a list of prices as strings: raw_data = ["$12.50", " 100.99 ", "N/A", "5.00"].
  • Loop through the list and:
    1. Strip the $ sign.
    2. Remove extra whitespace.
    3. Skip or handle strings like “N/A” so the code doesn’t crash.
    4. Convert the valid prices to floats and sum them.
import math

raw_data = ["$12.50", " 100.99 ", "N/A", "5.00"]
clean_prices = []

for item in raw_data:
    try:
        # Clean the string
        processed = item.replace("$", "").strip()
        # Attempt conversion
        clean_prices.append(float(processed))
    except ValueError:
        print(f"Skipping invalid data: {item}")

print(f"Total: {sum(clean_prices)}") # Output: 118.49


Lab:

The fsum Advantage

Goal: Learn how to sum a large list of floats without losing a single cent.

  • Create a list: values = [0.1] * 10.
  • Sum them using the standard sum(values).
  • Sum them using math.fsum(values).
  • Compare the results.
import math

values = [0.1] * 10

standard_sum = sum(values)
precise_sum = math.fsum(values)

print(f"Standard: {standard_sum}") # Output: 0.9999999999999999
print(f"fsum:     {precise_sum}")  # Output: 1.0

# EXPLANATION: math.fsum tracks the "lost" precision bits during 
# each addition, making it superior for summing long lists of floats.


Lab:

Bits and Bytes

Goal: Visualize how a float looks in its hex (hexadecimal) form.

  • Python floats have a .hex() method. Take the float 2.5 and 0.1 and print their hex strings.
  • Question: Why is the hex for 2.5 much shorter and “cleaner” than the hex for 0.1?
import math

### Solution 11: Hex Representation
print((2.5).hex()) # Output: '0x1.4000000000000p+1'
print((0.1).hex()) # Output: '0x1.999999999999ap-4' (Notice the repeating '9999...')

# EXPLANATION: 2.5 is a "dyadic" rational (the denominator is a power of 2),
# so it terminates in binary. 0.1 repeats infinitely, just like 1/3 in decimal.


Lab:

Comparison with Epsilon

Goal: Understand the “smallest possible” difference.

  • In numerical computing, we often compare a difference to a tiny value called Epsilon.
  • Calculate the absolute difference between (0.1 + 0.2) and 0.3.
  • Check if this difference is smaller than 1e-15.
import math

diff = abs((0.1 + 0.2) - 0.3)
epsilon = 1e-15

print(f"Difference: {diff}")
print(f"Is it negligible? {diff < epsilon}") # Output: True

# NOTE: This is essentially what math.isclose() does under the hood.



Lab:

List Filtering & Overflow

Goal: Handle a list containing “broken” float data.

  • Create a list: data = [1.5, float('inf'), 2.2, float('nan'), 3.7, -float('inf')].
  • Write a list comprehension to create a new list that only contains finite, real numbers (no inf or nan).
  • Hint: Use math.isfinite().
### Solution 13: Filtering with math.isfinite()
data = [1.5, float('inf'), 2.2, float('nan'), 3.7, -float('inf')]

# math.isfinite() returns False for NaN and both positive/negative Infinity
clean_data = [x for x in data if math.isfinite(x)]

print(f"Cleaned Data: {clean_data}") # Output: [1.5, 2.2, 3.7]

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top