Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 8

Exact Expressions and Logarithm Approximations in Python

Recommended resources

Hello! Welcome to the final lesson of our first module. In our previous lesson, we dove into the theoretical world of functions, learning to classify them as injective, surjective, or bijective. These concepts are abstract but form the bedrock for understanding mappings in algorithms and cryptography.

Today, we're shifting from the abstract to the concrete. We will explore how to perform mathematical computations in Python with the precision that computer science often demands. Your experience with Python will be a great asset here. Our goal is to master the evaluation of expressions involving integers, fractions, and powers, and to learn how to approximate logarithms to a specific level of accuracy. This is a crucial practical skill, as the subtle difference between exact and approximate arithmetic can be the difference between a correct algorithm and a failing one, especially in sensitive domains like cryptography.

A Quick Refresher on Python's Math Tools

Before we dive into the nuances of precision, let's quickly review the standard mathematical tools Python offers. You've likely used these before, but a brief recap will set the stage.

Math in Python is easy! 📐

This video from Bro Code provides a fast-paced overview of basic arithmetic and built-in math functions in Python. It's a great way to refresh your memory on the fundamentals.

Please watch the following sections: The first part on arithmetic operators, from the beginning up to the end of the modulus operator explanation. The section on built-in math functions like round(), abs(), and pow(), from this point. The final part on the math module, covering math.pi, math.sqrt(), math.ceil(), and math.floor(), from here.

This covers the basics: addition, subtraction, multiplication, division (/), integer division (//), modulus (%), and exponentiation (**). The math module adds more tools, but as we'll see, these all operate on standard number types that have important limitations.


The Pitfall of Floating-Point Arithmetic

One of the first "gotchas" that programmers encounter is that computers don't always handle decimal numbers perfectly. The standard float type in Python, like in most languages, uses a binary representation that cannot exactly represent many common decimal fractions.

This leads to small but significant precision errors. Let's see a classic demonstration.

Decimal Module in Python For Accurate Floats

This video from NeuralNine gets straight to the point, demonstrating the quintessential problem with floating-point math in Python.

Watch the introduction, from the start of the examples, to see why 0.1 + 0.2 == 0.3 surprisingly evaluates to False.

This happens because 0.1 and 0.2 are stored as binary approximations. When you add them, the small errors accumulate, and the result is not exactly 0.3. For many applications, this is fine. For financial calculations, scientific simulations, or cryptography, this is unacceptable.


Python's Solutions for Precision

Fortunately, Python provides powerful tools in its standard library to handle these situations.

1. Arbitrary-Precision Integers

First, the good news. Python's built-in int type automatically handles integers of any size, limited only by your computer's memory. This is a fantastic feature, especially for cryptography, which routinely involves numbers with hundreds of digits. You don't need a special library; it just works.

# A number far too large for standard integer types in languages like C++ or Java
large_power = 2**1024
print(large_power)

This ability to work with massive integers is fundamental to algorithms like RSA.

2. Exact Rational Arithmetic: The fractions Module

When you need to work with fractions and require perfect accuracy, the fractions module is the answer. It stores numbers as a numerator and a denominator, avoiding the representation issues of floating-point numbers entirely.

fractions — Rational numbers

The official Python documentation explains how to create and use Fraction objects.

Read the initial examples to see how to construct a Fraction from integers or a string. Notice how Fraction(1.1) does not give you 11/10, which reinforces the problem with float representations.

Let's see it in action. To compute exactly:

from fractions import Fraction

# Create Fraction objects. Using strings is safest to avoid float issues.
f1 = Fraction('1/3')
f2 = Fraction('2/7')

result = f1 + f2

# The result is automatically simplified
print(result) # Output: 13/21

# You can access the parts
print(result.numerator)   # Output: 13
print(result.denominator) # Output: 21

Any arithmetic you do with Fraction objects will be exact. This is perfect for theoretical calculations or algorithms where precision is paramount.

A particularly useful feature is finding the best rational approximation of a float.

fractions — Rational numbers

Now, let's look at a very useful method in the fractions module.

Find the description of the limit_denominator() method. The examples show how it can be used to recover a simple fraction from a messy float representation, like getting Fraction(1, 2) from the result of cos(pi/3).

This method is great for cleaning up imprecise data or recovering an intended rational number from a floating-point calculation.

3. High-Precision Decimals: The decimal Module

What if you need high precision, but you're working with decimals, not fractions? This is common in finance. For this, Python provides the decimal module. It allows you to define the exact precision you need for your calculations.

The most important rule when using decimal is to create Decimal objects from strings, not floats, to avoid carrying the floating-point error into your high-precision calculation.

Decimal Module in Python For Accurate Floats

The same video we saw earlier demonstrates how the decimal module solves the 0.1 + 0.2 problem and explains the critical difference between using strings and floats.

Watch these key segments: The solution using Decimal with string inputs (watch here). The demonstration of what goes wrong when you use float inputs (watch here). The explanation of setting the precision using the context (watch here).

To perform our problematic sum correctly, we do the following:

from decimal import Decimal, getcontext

# Set our desired precision (number of significant digits)
getcontext().prec = 50

# Create Decimal objects from strings
a = Decimal('0.1')
b = Decimal('0.2')
c = a + b

print(c) # Output: 0.3
print(c == Decimal('0.3')) # Output: True

Success! The decimal module gives us the control we need.


Powers and Logarithms with Precision

Now we can address the final part of our goal: handling powers and logarithms.

  • Powers: For integer exponents, Python's ** operator or pow() function works perfectly with int, Fraction, and Decimal types. For non-integer exponents, you'll often end up with an approximation. The decimal module can compute these to your desired precision.
  • Logarithms: The standard math.log() returns a float, which is an approximation. The decimal module, however, provides methods for high-precision logarithms.

Decimal fixed-point and floating-point arithmetic

The Decimal object has its own methods for transcendental functions like logarithms and square roots.

In the documentation, look at the code block under the heading "And some mathematical functions are also available to Decimal". This shows examples of .sqrt(), .exp(), .ln(), and .log10(). Then, scroll down to the API descriptions for ln() and log10() to confirm they operate using the context's precision.

Let's calculate the natural logarithm of 2 () to 40 decimal places.

from decimal import Decimal, getcontext

# Set precision to 40 digits
getcontext().prec = 40

# Calculate ln(2)
log_of_2 = Decimal(2).ln()

print(log_of_2)
# Output: 0.6931471805599453094172321214581765680755

This level of control is impossible with the standard math module.


Let's Practice

Time to put these tools to use. Solve the following problems using Python.

Problem 1: Exact Fraction Calculation
Calculate the exact value of . Print the final fraction.

Problem 2: Modular Exponentiation
A key operation in cryptography is modular exponentiation. Calculate .
Hint: The pow() function can take a third argument for the modulus: pow(base, exp, mod).

Problem 3: High-Precision Logarithm
Calculate the base-10 logarithm of 88 () to 60 significant digits.

Check your answers

Problem 1 Solution:

from fractions import Fraction

val1 = Fraction(19, 23)
val2 = Fraction(7, 31)
val3 = Fraction(4, 5)

result = (val1 - val2) * val3
print(result) # Output: 1332/3565

Problem 2 Solution:

# Python's pow(base, exp, mod) is highly optimized for this.
# No special libraries needed because the inputs and output are integers.
result = pow(57, 131, 103)
print(result) # Output: 81

Problem 3 Solution:

from decimal import Decimal, getcontext

# Set precision.
getcontext().prec = 60

# Create a Decimal object from a string and calculate the log.
num = Decimal('88')
result = num.log10()
print(result)
# Output: 1.94448267210633190234406981992015946409867187971439446215332

Conclusion

This lesson brought us from the world of abstract mathematics into the practical realm of computation. We've seen that while Python's basic tools are powerful, true precision requires choosing the right instrument for the job.

Key Takeaways:

  • Standard floating-point (float) arithmetic is inherently imprecise due to its binary representation.
  • Python's int type handles arbitrarily large integers, a crucial feature for fields like cryptography.
  • The fractions module provides exact arithmetic for rational numbers.
  • The decimal module provides user-defined, high-precision arithmetic for decimal numbers. Always construct Decimal objects from strings to ensure accuracy.
  • For high-precision powers and logarithms, the decimal module is the required tool.

You have now completed the first module, covering the essential language of logic, sets, relations, functions, and their computational counterparts. You are well-equipped for the next stage of our journey. In the next module, we will learn the formal art of mathematical proof. We will move beyond just using these concepts to proving that our claims about them are undeniably true.