Python & AI Tutorials Logo
Programação Python

4. Trabalhando com Números

No Capítulo 3, você aprendeu como criar variáveis e trabalhar com os tipos numéricos básicos do Python: inteiros e números de ponto flutuante. Agora é hora de colocar esses números em ação. Neste capítulo, você vai aprender a fazer cálculos, combinar operações e usar as ferramentas embutidas do Python para trabalhar com dados numéricos.

Os números são essenciais na programação. Seja calculando totais, processando medições, gerenciando estoque ou analisando dados, você vai precisar fazer operações aritméticas. Python torna operações numéricas diretas e intuitivas, mas há detalhes importantes para entender — especialmente sobre como os diferentes tipos de divisão funcionam, como os operadores interagem e como números de ponto flutuante se comportam.

Ao final deste capítulo, você estará à vontade para fazer cálculos, entender o comportamento dos operadores e usar as funções numéricas do Python para resolver problemas do mundo real.

4.1) Aritmética Básica: Adição, Subtração e Multiplicação

Vamos começar com as operações aritméticas mais fundamentais. Python usa símbolos familiares para a matemática básica, e essas operações funcionam exatamente como você espera da aritmética do dia a dia.

4.1.1) Adição com o Operador +

O operador + soma dois números. Ele funciona tanto com inteiros quanto com números de ponto flutuante:

python
# basic_addition.py
# Adding integers
total = 15 + 27
print(total)  # Output: 42
 
# Adding floats
price = 19.99 + 5.50
print(price)  # Output: 25.49
 
# You can add multiple numbers in one expression
sum_total = 10 + 20 + 30 + 40
print(sum_total)  # Output: 100

A adição é direta, mas há um detalhe importante: quando você soma um inteiro e um float, o Python converte automaticamente o resultado para float para preservar a precisão decimal:

python
# mixed_addition.py
result = 10 + 3.5
print(result)  # Output: 13.5
print(type(result))  # Output: <class 'float'>

Essa conversão automática acontece porque um float pode representar tanto números inteiros quanto decimais, enquanto um inteiro não pode representar decimais. Python escolhe o tipo que não perde informação.

4.1.2) Subtração com o Operador -

O operador - subtrai o segundo número do primeiro:

python
# basic_subtraction.py
# Subtracting integers
difference = 100 - 42
print(difference)  # Output: 58
 
# Subtracting floats
remaining = 50.75 - 12.25
print(remaining)  # Output: 38.5
 
# Subtraction can produce negative results
balance = 20 - 35
print(balance)  # Output: -15

Assim como na adição, a subtração também promove inteiros para floats quando você mistura tipos:

python
# mixed_subtraction.py
result = 100 - 0.01
print(result)  # Output: 99.99
print(type(result))  # Output: <class 'float'>

4.1.3) Multiplicação com o Operador *

O operador * multiplica dois números:

python
# basic_multiplication.py
# Multiplying integers
product = 6 * 7
print(product)  # Output: 42
 
# Multiplying floats
area = 3.5 * 2.0
print(area)  # Output: 7.0
 
# Multiplying by zero always gives zero
result = 1000 * 0
print(result)  # Output: 0

A multiplicação segue as mesmas regras de conversão de tipo. Quando você multiplica um inteiro por um float, o resultado é um float:

python
# mixed_multiplication.py
result = 5 * 2.5
print(result)  # Output: 12.5
print(type(result))  # Output: <class 'float'>
 
# Even if the result is a whole number
result = 4 * 2.0
print(result)  # Output: 8.0 (note the .0)
print(type(result))  # Output: <class 'float'>

Repare que, no último exemplo, mesmo que 4 × 2.0 seja igual a 8, o Python o representa como 8.0 porque um dos operandos era um float. O tipo do resultado depende dos tipos das entradas, não de o resultado matemático ser ou não um número inteiro.

4.1.4) Exemplos Práticos com Aritmética Básica

Vamos ver como essas operações funcionam juntas em cenários realistas:

python
# shopping_cart.py
# Calculate a shopping cart total
item1_price = 12.99
item2_price = 8.50
item3_price = 15.00
 
subtotal = item1_price + item2_price + item3_price
print(f"Subtotal: ${subtotal}")  # Output: Subtotal: $36.49
 
tax_rate = 0.08
tax = subtotal * tax_rate
print(f"Tax: ${tax}")  # Output: Tax: $2.9192
 
total = subtotal + tax
print(f"Total: ${total}")  # Output: Total: $39.4092
python
# temperature_change.py
# Calculate temperature change
morning_temp = 45.5
afternoon_temp = 68.2
 
change = afternoon_temp - morning_temp
print(f"Temperature increased by {change} degrees")
# Output: Temperature increased by 22.7 degrees

Essas operações básicas formam a base para cálculos mais complexos. Entender como elas funcionam — especialmente como o Python lida com conversões de tipo — vai te ajudar a evitar surpresas quando seus programas fizerem cálculos.

4.2) Divisão, divisão por piso (floor) e resto: /, // e %

Divisão em Python é mais sutil do que adição, subtração ou multiplicação. Python fornece três operadores diferentes relacionados à divisão, cada um com um propósito distinto. Entender quando usar cada um é crucial para escrever programas corretos.

4.2.1) Divisão Comum com /

O operador / faz a "divisão verdadeira" — ele sempre retorna um resultado de ponto flutuante, mesmo ao dividir dois inteiros:

python
# true_division.py
# Dividing integers
result = 10 / 2
print(result)  # Output: 5.0 (note: float, not 5)
print(type(result))  # Output: <class 'float'>
 
# Division that doesn't result in a whole number
result = 10 / 3
print(result)  # Output: 3.3333333333333335
 
# Dividing floats
result = 15.5 / 2.5
print(result)  # Output: 6.2

O ponto-chave aqui é que / sempre produz um float, mesmo quando ambos os operandos são inteiros e o resultado é, matematicamente, um número inteiro. Isso é de propósito: o Python quer que a divisão se comporte de forma consistente e preserve a precisão decimal.

Esse comportamento é diferente de algumas outras linguagens de programação em que dividir dois inteiros produz um resultado inteiro. No Python 3, se você quiser um resultado inteiro, precisa usar divisão por piso (floor division, que veremos a seguir) ou converter o resultado explicitamente.

4.2.2) Divisão por piso (floor) com //

O operador // faz a "divisão por piso" (floor division) — ele divide e então arredonda para baixo até o inteiro mais próximo. Quando ambos os operandos são inteiros, o resultado é um inteiro. Quando qualquer operando é um float, o resultado é um float (mas ainda arredondado para baixo):

python
# floor_division.py
# Floor division with integers
result = 10 // 3
print(result)  # Output: 3 (not 3.333...)
print(type(result))  # Output: <class 'int'>
 
# Even division still gives an integer
result = 10 // 2
print(result)  # Output: 5 (integer, not 5.0)
print(type(result))  # Output: <class 'int'>
 
# Floor division with floats gives a float
result = 10.0 // 3
print(result)  # Output: 3.0
print(type(result))  # Output: <class 'float'>

"Arredondar para baixo" significa mover em direção ao menos infinito, não apenas remover a parte decimal. Isso importa para números negativos:

python
# floor_division_negative.py
# Positive numbers: rounds down (toward negative infinity)
result = 7 // 2
print(result)  # Output: 3
 
# Negative numbers: still rounds toward negative infinity
result = -7 // 2
print(result)  # Output: -4 (not -3!)
 
# Why -4? Because -3.5 rounded down (toward negative infinity) is -4
# Think of the number line: ... -4, -3.5, -3, -2, -1, 0, 1, 2, 3, 3.5, 4 ...

A divisão por piso é útil quando você precisa dividir itens em grupos ou calcular quantas unidades completas cabem em uma quantidade:

python
# floor_division_practical.py
# How many complete dozens in 50 eggs?
eggs = 50
eggs_per_dozen = 12
complete_dozens = eggs // eggs_per_dozen
print(f"Complete dozens: {complete_dozens}")  # Output: Complete dozens: 4
 
# How many full hours in 150 minutes?
minutes = 150
full_hours = minutes // 60
print(f"Full hours: {full_hours}")  # Output: Full hours: 2

4.2.3) Resto (Módulo) com %

O operador % (chamado de "módulo" ou "mod") retorna o resto após a divisão. Ele responde à pergunta: "Depois de dividir, o que sobrou?"

python
# modulo_basic.py
# 10 divided by 3 is 3 with remainder 1
result = 10 % 3
print(result)  # Output: 1
 
# 10 divided by 2 is 5 with remainder 0 (even division)
result = 10 % 2
print(result)  # Output: 0
 
# Works with floats too
result = 10.5 % 3
print(result)  # Output: 1.5

O operador módulo é extremamente útil em vários padrões comuns de programação:

Verificar se um número é par ou ímpar:

python
# even_odd_check.py
number = 17
 
if number % 2 == 0:
    print(f"{number} is even")
else:
    print(f"{number} is odd")
# Output: 17 is odd

Obter itens que sobraram:

python
# leftover_items.py
eggs = 50
eggs_per_dozen = 12
 
leftover = eggs % eggs_per_dozen
print(f"Leftover eggs: {leftover}")  # Output: Leftover eggs: 2

Fazer valores "darem a volta" dentro de um intervalo (como em um relógio):

python
# clock_arithmetic.py
# What hour is it 25 hours from now? (on a 12-hour clock)
current_hour = 10
hours_later = 25
future_hour = (current_hour + hours_later) % 12
print(f"Hour: {future_hour}")  # Output: Hour: 11

4.2.4) Relação Entre //, % e /

A divisão por piso e o módulo são intimamente relacionados. Juntos, eles te dão o resultado completo de uma divisão:

python
# division_relationship.py
dividend = 17
divisor = 5
 
quotient = dividend // divisor  # How many times 5 goes into 17
remainder = dividend % divisor  # What's left over
 
print(f"{dividend} ÷ {divisor} = {quotient} remainder {remainder}")
# Output: 17 ÷ 5 = 3 remainder 2
 
# You can verify: quotient * divisor + remainder should equal dividend
verification = quotient * divisor + remainder
print(f"Verification: {quotient} × {divisor} + {remainder} = {verification}")
# Output: Verification: 3 × 5 + 2 = 17

Essa relação é sempre verdadeira: para quaisquer números a e b (onde b não é zero), a == (a // b) * b + (a % b).

4.2.5) Escolhendo o Operador de Divisão Certo

Aqui vai um guia rápido para escolher qual operador usar:

  • Use / quando você precisa do resultado matemático exato com decimais (mais comum em cálculos)
  • Use // quando você precisa contar quantos grupos completos cabem (como dúzias, horas, páginas)
  • Use % quando você precisa do resto ou da quantidade que sobrou (como verificar par/ímpar, dar volta em intervalos, achar sobras)
python
# choosing_operators.py
# Scenario: Distributing 47 candies among 6 children
 
candies = 47
children = 6
 
# How many candies per child? (use //)
per_child = candies // children
print(f"Each child gets {per_child} candies")  # Output: Each child gets 7 candies
 
# How many candies are left over? (use %)
leftover = candies % children
print(f"Leftover candies: {leftover}")  # Output: Leftover candies: 5
 
# What's the exact average? (use /)
average = candies / children
print(f"Average per child: {average}")  # Output: Average per child: 7.833333333333333

4.3) Operadores de Atribuição Composta

Ao programar, você vai frequentemente precisar atualizar uma variável realizando uma operação sobre o valor atual dela. Por exemplo, somar a um total acumulado, subtrair de um saldo ou multiplicar um valor por um fator. Python fornece uma forma concisa de fazer isso com operadores de atribuição composta.

4.3.1) O Que São Operadores de Atribuição Composta

Um operador de atribuição composta combina uma operação aritmética com a atribuição. Em vez de escrever:

python
# traditional_update.py
count = 10
count = count + 5  # Add 5 to count
print(count)  # Output: 15

Você pode escrever:

python
# augmented_update.py
count = 10
count += 5  # Same as: count = count + 5
print(count)  # Output: 15

Ambas as versões fazem exatamente a mesma coisa, mas a versão com operador composto é mais concisa e expressa claramente a intenção: "aumentar count em 5".

4.3.2) Todos os Operadores de Atribuição Composta

Python fornece operadores de atribuição composta para todas as operações aritméticas:

python
# all_augmented_operators.py
# Addition
x = 10
x += 5  # x = x + 5
print(f"After += 5: {x}")  # Output: After += 5: 15
 
# Subtraction
x = 10
x -= 3  # x = x - 3
print(f"After -= 3: {x}")  # Output: After -= 3: 7
 
# Multiplication
x = 10
x *= 4  # x = x * 4
print(f"After *= 4: {x}")  # Output: After *= 4: 40
 
# Division
x = 10
x /= 2  # x = x / 2
print(f"After /= 2: {x}")  # Output: After /= 2: 5.0
 
# Floor division
x = 10
x //= 3  # x = x // 3
print(f"After //= 3: {x}")  # Output: After //= 3: 3
 
# Modulo
x = 10
x %= 3  # x = x % 3
print(f"After %= 3: {x}")  # Output: After %= 3: 1
 
# Exponentiation (we'll see more about ** in section 4.6)
x = 2
x **= 3  # x = x ** 3 (2 to the power of 3)
print(f"After **= 3: {x}")  # Output: After **= 3: 8

4.3.3) Casos de Uso Comuns para Atribuição Composta

Operadores de atribuição composta brilham em vários padrões comuns de programação:

Acumular um total:

python
# accumulating_total.py
total = 0
total += 10  # Add first item
total += 25  # Add second item
total += 15  # Add third item
print(f"Total: {total}")  # Output: Total: 50

Contar ocorrências:

python
# counting.py
count = 0
# Imagine these happen as we process data
count += 1  # Found one
count += 1  # Found another
count += 1  # Found another
print(f"Count: {count}")  # Output: Count: 3

Atualizar um saldo:

python
# balance_updates.py
balance = 100.00
balance -= 25.50  # Purchase
balance += 50.00  # Deposit
balance -= 10.00  # Purchase
print(f"Balance: ${balance}")  # Output: Balance: $114.5

Aplicar operações repetidas:

python
# repeated_operations.py
value = 100
value *= 1.1  # Increase by 10%
value *= 1.1  # Increase by 10% again
value *= 1.1  # Increase by 10% again
print(f"Value after three 10% increases: {value}")
# Output: Value after three 10% increases: 133.10000000000002

4.3.4) Detalhes Importantes Sobre Atribuição Composta

A atribuição composta cria um novo objeto para tipos imutáveis:

Lembre-se do Capítulo 3 que números são imutáveis — você não pode mudar o valor de um número, apenas criar novos números. Quando você escreve x += 5, o Python cria um novo objeto numérico e o atribui a x. O objeto antigo é descartado (vamos explorar esse conceito mais a fundo no Capítulo 17, quando falarmos do modelo de objetos do Python):

python
# augmented_with_immutables.py
x = 10
print(id(x))  # Output: (some memory address)
 
x += 5
print(id(x))  # Output: (different memory address)

Por enquanto, apenas entenda que x += 5 é equivalente a x = x + 5 — é um atalho conveniente, não uma operação fundamentalmente diferente.

Você não pode usar atribuição composta antes de a variável existir:

python
# augmented_requires_existing.py
# This will cause an error:
# count += 1  # NameError: name 'count' is not defined
 
# You must initialize the variable first:
count = 0
count += 1  # Now this works
print(count)  # Output: 1

Conversões de tipo acontecem da mesma forma:

python
# augmented_type_conversion.py
x = 10  # integer
x += 2.5  # Add a float
print(x)  # Output: 12.5
print(type(x))  # Output: <class 'float'>

O resultado segue as mesmas regras de conversão de tipo que os operadores normais: se você mistura inteiros e floats, o resultado é um float.

4.3.5) Quando Usar Atribuição Composta

Use operadores de atribuição composta sempre que estiver atualizando uma variável com base em seu valor atual. Eles tornam seu código:

  • Mais conciso: x += 5 é mais curto do que x = x + 5
  • Mais legível: A intenção fica imediatamente clara
  • Menos propenso a erros: Você não corre o risco de digitar o nome da variável errado

Compare estas duas versões:

python
# comparison.py
# Without augmented assignment
accumulated_distance_in_kilometers = 0
accumulated_distance_in_kilometers = accumulated_distance_in_kilometers + 10
accumulated_distance_in_kilometers = accumulated_distance_in_kilometers + 25
 
# With augmented assignment
accumulated_distance_in_kilometers = 0
accumulated_distance_in_kilometers += 10
accumulated_distance_in_kilometers += 25

A versão com atribuição composta é mais clara e tem menos chance de erros de digitação. Operadores de atribuição composta são um recurso pequeno, mas são usados constantemente em código Python real.

4.4) Precedência de Operadores e Parênteses

Quando você combina várias operações em uma única expressão, o Python precisa de regras para determinar a ordem de avaliação. 2 + 3 * 4 deve ser calculado como (2 + 3) * 4 = 20 ou como 2 + (3 * 4) = 14? A resposta depende da precedência de operadores — as regras que determinam quais operações acontecem primeiro.

4.4.1) Entendendo Precedência de Operadores

Python segue as mesmas regras de precedência que você aprendeu em matemática: multiplicação e divisão acontecem antes de adição e subtração. Isso muitas vezes é lembrado pelo acrônimo PEMDAS (Parênteses, Expoentes, Multiplicação/Divisão, Adição/Subtração), embora veremos expoentes na seção 4.6.

python
# precedence_basic.py
# Multiplication happens before addition
result = 2 + 3 * 4
print(result)  # Output: 14 (not 20)
# Python calculates: 2 + (3 * 4) = 2 + 12 = 14
 
# Division happens before subtraction
result = 10 - 8 / 2
print(result)  # Output: 6.0 (not 1.0)
# Python calculates: 10 - (8 / 2) = 10 - 4.0 = 6.0

Aqui está a ordem de precedência para os operadores que aprendemos até agora (do mais alto para o mais baixo):

  1. Parênteses () — maior precedência, sempre avaliados primeiro
  2. Exponenciação ** — (vamos ver na seção 4.6)
  3. Multiplicação, Divisão, Divisão por piso, Módulo *, /, //, % — mesmo nível, avaliados da esquerda para a direita
  4. Adição, Subtração +, - — mesmo nível, avaliados da esquerda para a direita

Vamos ver mais exemplos de como a precedência funciona:

python
# precedence_examples.py
# Multiplication before addition
result = 5 + 2 * 3
print(result)  # Output: 11 (5 + 6)
 
# Division before subtraction
result = 20 - 10 / 2
print(result)  # Output: 15.0 (20 - 5.0)
 
# Multiple operations at the same level: left to right
result = 10 - 3 + 2
print(result)  # Output: 9 ((10 - 3) + 2)
 
result = 20 / 4 * 2
print(result)  # Output: 10.0 ((20 / 4) * 2)

4.4.2) Usando Parênteses para Controlar a Ordem

Parênteses sobrescrevem a precedência padrão. Operações dentro de parênteses são sempre executadas primeiro, independentemente dos operadores envolvidos:

python
# parentheses_override.py
# Without parentheses: multiplication first
result = 2 + 3 * 4
print(result)  # Output: 14
 
# With parentheses: addition first
result = (2 + 3) * 4
print(result)  # Output: 20
 
# Another example
result = 10 - 8 / 2
print(result)  # Output: 6.0
 
result = (10 - 8) / 2
print(result)  # Output: 1.0

Você pode aninhar parênteses para criar expressões mais complexas. O Python avalia de dentro para fora, começando pelos parênteses mais internos:

python
# nested_parentheses.py
result = ((2 + 3) * 4) - 1
print(result)  # Output: 19
# Step 1: (2 + 3) = 5
# Step 2: (5 * 4) = 20
# Step 3: 20 - 1 = 19
 
result = 2 * (3 + (4 - 1))
print(result)  # Output: 12
# Step 1: (4 - 1) = 3
# Step 2: (3 + 3) = 6
# Step 3: 2 * 6 = 12

4.4.3) Quando Operadores Têm a Mesma Precedência

Quando vários operadores no mesmo nível de precedência aparecem em uma expressão, o Python os avalia da esquerda para a direita (isso é chamado de "associatividade à esquerda"):

python
# left_to_right.py
# Addition and subtraction: left to right
result = 10 - 3 + 2 - 1
print(result)  # Output: 8
# Evaluation: ((10 - 3) + 2) - 1 = (7 + 2) - 1 = 9 - 1 = 8
 
# Multiplication and division: left to right
result = 20 / 4 * 2
print(result)  # Output: 10.0
# Evaluation: (20 / 4) * 2 = 5.0 * 2 = 10.0
 
# This matters! Different order gives different result:
result = 20 / (4 * 2)
print(result)  # Output: 2.5

4.4.4) Exemplos Práticos com Precedência

Vamos olhar cenários realistas onde entender precedência é importante:

python
# temperature_conversion.py
# Convert Fahrenheit to Celsius: C = (F - 32) * 5 / 9
fahrenheit = 98.6
 
# Correct: parentheses ensure subtraction happens first
celsius = (fahrenheit - 32) * 5 / 9
print(f"{fahrenheit}°F = {celsius}°C")
# Output: 98.6°F = 37.0°C
 
# Wrong: without parentheses, multiplication happens first
# celsius = fahrenheit - 32 * 5 / 9  # This would be wrong!
# This would calculate: fahrenheit - ((32 * 5) / 9)
python
# calculate_average.py
# Calculate average of three numbers
num1 = 85
num2 = 92
num3 = 78
 
# Correct: parentheses ensure addition happens before division
average = (num1 + num2 + num3) / 3
print(f"Average: {average}")  # Output: Average: 85.0
 
# Wrong: without parentheses, only num3 is divided by 3
# average = num1 + num2 + num3 / 3  # This would be wrong!
# This would calculate: 85 + 92 + (78 / 3) = 85 + 92 + 26.0 = 203.0
python
# discount_calculation.py
# Calculate price after discount and tax
original_price = 100.00
discount_rate = 0.20
tax_rate = 0.08
 
# Calculate discount amount
discount = original_price * discount_rate
 
# Calculate price after discount
discounted_price = original_price - discount
 
# Calculate final price with tax
final_price = discounted_price * (1 + tax_rate)
print(f"Final price: ${final_price}")  # Output: Final price: $86.4
 
# Or in one expression (using parentheses to be clear):
final_price = (original_price * (1 - discount_rate)) * (1 + tax_rate)
print(f"Final price: ${final_price}")  # Output: Final price: $86.4

4.4.5) Boas Práticas para Precedência de Operadores

Use parênteses para clareza, mesmo quando não forem estritamente necessários:

Às vezes, adicionar parênteses torna sua intenção mais clara, mesmo que eles não mudem o resultado:

python
# clarity_with_parentheses.py
# These are equivalent, but the second is clearer:
result = 2 + 3 * 4
result = 2 + (3 * 4)  # Clearer: shows you know multiplication happens first
 
# Complex expressions benefit from parentheses:
result = (subtotal * tax_rate) + (subtotal * tip_rate)  # Clear intent

Divida expressões complexas em etapas:

Quando uma expressão fica muito complexa, muitas vezes é melhor dividi-la em várias linhas:

python
# breaking_into_steps.py
# Instead of this complex one-liner:
result = ((price * quantity) * (1 - discount)) * (1 + tax)
 
# Consider breaking it into steps:
subtotal = price * quantity
discounted = subtotal * (1 - discount)
final = discounted * (1 + tax)
result = final

A versão em etapas é mais fácil de ler, debugar e verificar. Não sacrifique clareza por concisão.

Quando estiver em dúvida, use parênteses:

Se você não tiver certeza sobre a precedência, adicione parênteses. O interpretador Python não vai se importar, e você no futuro (ou outras pessoas lendo seu código) vão agradecer.

Entender a precedência de operadores te ajuda a escrever expressões corretas e a ler o código de outras pessoas. O princípio chave: ao combinar operações, pense na ordem de avaliação e use parênteses para tornar sua intenção explícita.

4.5) Misturando Inteiros e Floats em Expressões

Você já viu que o Python lida automaticamente com a mistura de inteiros e floats em operações simples. Agora vamos explorar esse comportamento de forma mais sistemática e entender as regras que governam a conversão de tipos em expressões numéricas.

4.5.1) A Regra de Promoção de Tipo

Quando o Python realiza uma operação aritmética envolvendo um inteiro e um float, ele converte automaticamente (ou "promove") o inteiro para float antes de executar a operação. O resultado é sempre um float:

python
# type_promotion.py
# Integer + Float = Float
result = 10 + 3.5
print(result)  # Output: 13.5
print(type(result))  # Output: <class 'float'>
 
# Float + Integer = Float (order doesn't matter)
result = 3.5 + 10
print(result)  # Output: 13.5
print(type(result))  # Output: <class 'float'>
 
# This applies to all arithmetic operators
result = 5 * 2.0
print(result)  # Output: 10.0 (float, not int)
print(type(result))  # Output: <class 'float'>

Por que o Python faz isso? Porque floats podem representar tanto números inteiros quanto decimais, enquanto inteiros não podem representar decimais. Converter para float preserva todas as informações e evita perda de precisão.

Aqui está uma representação visual de como o Python decide o tipo de resultado:

Yes

No

Yes

No

Operation with
int and float

Any operand
is float?

Result is float

Division
operator /?

Result is int

4.5.2) Promoção de Tipo em Expressões Complexas

Quando uma expressão contém múltiplas operações com tipos mistos, o Python aplica a regra de promoção em cada etapa:

python
# complex_mixed_types.py
# Multiple operations with mixed types
result = 10 + 3.5 * 2
print(result)  # Output: 17.0
print(type(result))  # Output: <class 'float'>
 
# What happens step by step:
# 1. 3.5 * 2 → 3.5 * 2.0 (2 promoted to float) → 7.0 (float)
# 2. 10 + 7.0 → 10.0 + 7.0 (10 promoted to float) → 17.0 (float)
 
# Another example
result = 5 / 2 + 3
print(result)  # Output: 5.5
print(type(result))  # Output: <class 'float'>
 
# Step by step:
# 1. 5 / 2 → 2.5 (division always produces float)
# 2. 2.5 + 3 → 2.5 + 3.0 (3 promoted to float) → 5.5 (float)

Uma vez que qualquer operação em uma expressão produz um float, todas as operações subsequentes envolvendo esse resultado também produzirão floats.

4.5.3) Caso Especial: Divisão Comum Sempre Produz Floats

Lembre-se da seção 4.2 que o operador / sempre produz um float, mesmo ao dividir dois inteiros:

python
# division_always_float.py
# Even when the result is a whole number
result = 10 / 2
print(result)  # Output: 5.0 (not 5)
print(type(result))  # Output: <class 'float'>
 
# This means any expression with / will have a float result
result = 10 / 2 + 3  # 5.0 + 3 → 5.0 + 3.0 → 8.0
print(result)  # Output: 8.0
print(type(result))  # Output: <class 'float'>

Se você quiser um resultado inteiro da divisão, use divisão por piso // em vez disso (mas lembre que ela arredonda para baixo):

python
# floor_division_integer.py
# Floor division with integers produces an integer
result = 10 // 2
print(result)  # Output: 5 (integer)
print(type(result))  # Output: <class 'int'>
 
# But floor division with any float produces a float
result = 10.0 // 2
print(result)  # Output: 5.0 (float)
print(type(result))  # Output: <class 'float'>

4.5.4) Implicações Práticas de Misturar Tipos

Entender promoção de tipo te ajuda a prever e controlar os tipos dos seus resultados:

python
# practical_type_mixing.py
# Calculating price per item
total_cost = 47.50
num_items = 5
 
price_per_item = total_cost / num_items  # Float / int → float
print(f"Price per item: ${price_per_item}")
# Output: Price per item: $4.75
 
# Calculating average (will be float even if inputs are integers)
total_points = 450
num_tests = 5
 
average = total_points / num_tests  # Int / int → float
print(f"Average: {average}")  # Output: Average: 90.0
 
# If you need an integer result, convert explicitly
average_rounded = int(total_points / num_tests)
print(f"Average (as integer): {average_rounded}")
# Output: Average (as integer): 90

4.5.5) Quando Resultados Inteiros Importam

Às vezes você precisa especificamente de resultados inteiros para contagem, indexação ou outras operações discretas. Aqui está como garantir que você obtenha inteiros:

python
# ensuring_integer_results.py
# Using floor division when you need integer results
items = 47
items_per_box = 12
 
# How many complete boxes?
complete_boxes = items // items_per_box  # Integer result
print(f"Complete boxes: {complete_boxes}")
# Output: Complete boxes: 3
 
# If you use regular division, you get a float
boxes_float = items / items_per_box
print(f"Boxes (float): {boxes_float}")
# Output: Boxes (float): 3.9166666666666665
 
# Converting float to integer (truncates toward zero)
boxes_truncated = int(boxes_float)
print(f"Boxes (truncated): {boxes_truncated}")
# Output: Boxes (truncated): 3

Note a diferença: // arredonda para baixo (em direção ao menos infinito), enquanto int() trunca em direção a zero. Para números positivos eles são iguais, mas para números negativos diferem:

python
# truncation_vs_floor.py
# For positive numbers: same result
print(7 // 2)    # Output: 3
print(int(7/2))  # Output: 3
 
# For negative numbers: different results
print(-7 // 2)    # Output: -4 (rounds down toward negative infinity)
print(int(-7/2))  # Output: -3 (truncates toward zero)

4.5.6) Evitando Resultados Float Inesperados

Às vezes você pode se surpreender ao obter um float quando esperava um inteiro. Isso geralmente acontece por causa de divisão ou mistura de tipos:

python
# unexpected_floats.py
# Calculating average - result is always float because of division
count = 10
total = 100
average = total / count
print(average)  # Output: 10.0 (float, even though it's a whole number)
 
# If you need an integer and you know it divides evenly:
average_int = total // count
print(average_int)  # Output: 10 (integer)
 
# Or convert explicitly:
average_int = int(total / count)
print(average_int)  # Output: 10 (integer)

A ideia principal: as regras de promoção de tipo do Python são projetadas para preservar precisão e evitar perda de dados. Quando você mistura inteiros e floats, ou quando usa divisão comum, espere resultados float. Se você precisar de inteiros, use divisão por piso ou conversão explícita, mas fique atento a como elas lidam com arredondamento.

4.6) Funções Numéricas Embutidas Úteis: abs(), min(), max() e pow()

Python fornece várias funções embutidas que realizam operações numéricas comuns. Essas funções funcionam com inteiros e floats, e são ferramentas essenciais para a programação do dia a dia. Vamos explorar as mais usadas.

4.6.1) Valor Absoluto com abs()

A função abs() retorna o valor absoluto de um número — sua distância até zero, ignorando o sinal:

python
# absolute_value.py
# Absolute value of negative numbers
result = abs(-42)
print(result)  # Output: 42
 
# Absolute value of positive numbers (unchanged)
result = abs(42)
print(result)  # Output: 42
 
# Works with floats too
result = abs(-3.14)
print(result)  # Output: 3.14
 
# Absolute value of zero is zero
result = abs(0)
print(result)  # Output: 0

A função de valor absoluto é útil sempre que você se importa com a magnitude, mas não com a direção:

python
# practical_abs.py
# Calculate temperature difference (magnitude only)
morning_temp = 45.5
evening_temp = 38.2
 
difference = abs(evening_temp - morning_temp)
print(f"Temperature changed by {difference} degrees")
# Output: Temperature changed by 7.3 degrees
 
# Calculate distance between two points (always positive)
position1 = 10
position2 = 25
 
distance = abs(position2 - position1)
print(f"Distance: {distance}")  # Output: Distance: 15
 
# Works the same regardless of order
distance = abs(position1 - position2)
print(f"Distance: {distance}")  # Output: Distance: 15

4.6.2) Encontrando Mínimo e Máximo com min() e max()

A função min() retorna o menor de seus argumentos, e max() retorna o maior:

python
# min_max_basic.py
# Find minimum of two numbers
smallest = min(10, 25)
print(smallest)  # Output: 10
 
# Find maximum of two numbers
largest = max(10, 25)
print(largest)  # Output: 25
 
# Works with more than two arguments
smallest = min(5, 12, 3, 18, 7)
print(smallest)  # Output: 3
 
largest = max(5, 12, 3, 18, 7)
print(largest)  # Output: 18
 
# Works with floats and mixed types
smallest = min(3.5, 2, 4.1, 1.9)
print(smallest)  # Output: 1.9

Essas funções são valiosíssimas para encontrar extremos em dados:

python
# practical_min_max.py
# Find the best and worst test scores
test1 = 85
test2 = 92
test3 = 78
test4 = 95
 
highest_score = max(test1, test2, test3, test4)
lowest_score = min(test1, test2, test3, test4)
 
print(f"Highest score: {highest_score}")  # Output: Highest score: 95
print(f"Lowest score: {lowest_score}")    # Output: Lowest score: 78
 
# Clamp a value within a range
value = 150
minimum = 0
maximum = 100
 
# Ensure value is not below minimum
value = max(value, minimum)
# Ensure value is not above maximum
value = min(value, maximum)
 
print(f"Clamped value: {value}")  # Output: Clamped value: 100

O padrão de "clamp" (usar max() e min() juntos) é comum quando você precisa restringir um valor a um intervalo específico:

python
# clamping_pattern.py
def clamp(value, min_value, max_value):
    """Constrain value to be within [min_value, max_value]"""
    return max(min_value, min(value, max_value))
 
# Test the clamp function
print(clamp(150, 0, 100))   # Output: 100 (too high, clamped to max)
print(clamp(-10, 0, 100))   # Output: 0 (too low, clamped to min)
print(clamp(50, 0, 100))    # Output: 50 (within range, unchanged)

4.6.3) Exponenciação com pow()

A função pow() eleva um número a uma potência. Python também tem o operador ** para exponenciação, que é mais usado, mas pow() oferece recursos adicionais:

python
# exponentiation.py
# Using the ** operator (most common)
result = 2 ** 3  # 2 to the power of 3
print(result)  # Output: 8
 
result = 5 ** 2  # 5 squared
print(result)  # Output: 25
 
# Using pow() function (equivalent)
result = pow(2, 3)
print(result)  # Output: 8
 
result = pow(5, 2)
print(result)  # Output: 25
 
# Negative exponents give fractions
result = 2 ** -3  # 1 / (2^3) = 1/8
print(result)  # Output: 0.125
 
# Fractional exponents give roots
result = 9 ** 0.5  # Square root of 9
print(result)  # Output: 3.0
 
result = 8 ** (1/3)  # Cube root of 8
print(result)  # Output: 2.0

A função pow() pode receber um terceiro argumento opcional para exponenciação modular (útil em criptografia e teoria dos números, mas além do escopo da aritmética básica):

python
# modular_exponentiation.py
# pow(base, exponent, modulus) computes (base ** exponent) % modulus efficiently
result = pow(2, 10, 100)  # (2^10) % 100
print(result)  # Output: 24
 
# This is more efficient than computing separately for large numbers:
# result = (2 ** 10) % 100  # Same result, but less efficient for large numbers

Para a maioria dos usos do dia a dia, o operador ** é mais conveniente do que pow():

python
# practical_exponentiation.py
# Calculate compound interest: A = P(1 + r)^t
principal = 1000  # Initial amount
rate = 0.05       # 5% interest rate
years = 10
 
amount = principal * (1 + rate) ** years
print(f"Amount after {years} years: ${amount:.2f}")
# Output: Amount after 10 years: $1628.89
 
# Calculate area of a square
side_length = 5
area = side_length ** 2
print(f"Area: {area}")  # Output: Area: 25
 
# Calculate volume of a cube
side_length = 3
volume = side_length ** 3
print(f"Volume: {volume}")  # Output: Volume: 27

4.6.4) Combinando Funções Embutidas

Essas funções funcionam bem juntas para resolver problemas comuns:

python
# combining_functions.py
# Find the range (difference between max and min)
values = [15, 42, 8, 23, 37]
value_range = max(values) - min(values)
print(f"Range: {value_range}")  # Output: Range: 34
 
# Note: We're using a list here (we'll learn about lists in detail in Chapter 13)
# For now, just understand that max() and min() can work with a list of values
 
# Calculate distance between two points in 2D space
x1, y1 = 3, 4
x2, y2 = 6, 8
 
# Distance formula: sqrt((x2-x1)^2 + (y2-y1)^2)
# We'll use ** for squaring (square root comes in section 4.11)
distance_squared = (x2 - x1) ** 2 + (y2 - y1) ** 2
distance = distance_squared ** 0.5  # Square root via fractional exponent
print(f"Distance: {distance}")  # Output: Distance: 5.0

Essas funções embutidas são ferramentas fundamentais na programação em Python. Elas são eficientes, bem testadas e funcionam de maneira consistente em diferentes tipos numéricos. Use-as sempre que precisar dessas operações comuns — não há necessidade de escrever suas próprias implementações.

4.7) Arredondando Números com round()

Ao trabalhar com números de ponto flutuante, você frequentemente precisa arredondar resultados para um certo número de casas decimais para exibição, cálculos ou armazenamento. A função round() do Python fornece essa capacidade.

4.7.1) Arredondamento Básico com round()

A função round() recebe um número e o arredonda para o inteiro mais próximo:

python
# basic_rounding.py
# Round to nearest integer
result = round(3.7)
print(result)  # Output: 4
 
result = round(3.2)
print(result)  # Output: 3
 
# Exactly halfway rounds to nearest even number (banker's rounding)
result = round(2.5)
print(result)  # Output: 2 (rounds to even)
 
result = round(3.5)
print(result)  # Output: 4 (rounds to even)
 
# Negative numbers work too
result = round(-3.7)
print(result)  # Output: -4
 
result = round(-3.2)
print(result)  # Output: -3

Repare no comportamento ao arredondar números exatamente no meio entre dois inteiros (como 2.5 ou 3.5). Python usa "round half to even" (também chamado de "arredondamento do banqueiro"), que arredonda para o inteiro par mais próximo. Isso reduz viés em operações de arredondamento repetidas. Para a maioria dos usos do dia a dia, esse detalhe raramente importa, mas é bom estar ciente.

4.7.2) Arredondando para um Número Específico de Casas Decimais

A função round() aceita um segundo argumento opcional especificando quantas casas decimais manter:

python
# decimal_places.py
# Round to 2 decimal places
result = round(3.14159, 2)
print(result)  # Output: 3.14
 
# Round to 1 decimal place
result = round(3.14159, 1)
print(result)  # Output: 3.1
 
# Round to 3 decimal places
result = round(2.71828, 3)
print(result)  # Output: 2.718
 
# You can round to 0 decimal places (same as omitting the argument)
result = round(3.7, 0)
print(result)  # Output: 4.0 (note: returns float, not int)

Quando você especifica casas decimais, round() retorna um float (mesmo se você arredondar para 0 casas decimais). Quando você omite o segundo argumento, ele retorna um inteiro.

4.7.3) Usos Práticos de Arredondamento

Arredondar é essencial para exibir valores monetários, medições e outros valores em que precisão excessiva é desnecessária ou confusa:

python
# practical_rounding.py
# Display prices with 2 decimal places
price = 19.99
tax_rate = 0.08
total = price * (1 + tax_rate)
 
print(f"Total (unrounded): ${total}")  # Output: Total (unrounded): $21.5892
print(f"Total (rounded): ${round(total, 2)}")  # Output: Total (rounded): $21.59
 
# Calculate and display average
total_score = 456
num_tests = 7
average = total_score / num_tests
 
print(f"Average (unrounded): {average}")  # Output: Average (unrounded): 65.14285714285714
print(f"Average (rounded): {round(average, 2)}")  # Output: Average (rounded): 65.14
 
# Round measurements to reasonable precision
distance_meters = 123.456789
distance_rounded = round(distance_meters, 1)
print(f"Distance: {distance_rounded} meters")  # Output: Distance: 123.5 meters

4.7.4) Arredondar vs Truncar vs Floor/Ceiling

É importante entender que arredondar é diferente de truncar (remover casas decimais) ou das operações floor/ceiling:

python
# rounding_vs_others.py
value = 3.7
 
# Rounding: nearest integer
rounded = round(value)
print(f"Rounded: {rounded}")  # Output: Rounded: 4
 
# Truncating: remove decimal part (convert to int)
truncated = int(value)
print(f"Truncated: {truncated}")  # Output: Truncated: 3
 
# We'll see floor and ceil in section 4.11, but briefly:
# Floor: largest integer <= value (always rounds down)
# Ceiling: smallest integer >= value (always rounds up)

Para números negativos, as diferenças ficam mais marcantes:

python
# negative_rounding.py
value = -3.7
 
# Rounding: nearest integer
rounded = round(value)
print(f"Rounded: {rounded}")  # Output: Rounded: -4
 
# Truncating: toward zero
truncated = int(value)
print(f"Truncated: {truncated}")  # Output: Truncated: -3
 
# Floor (rounds down toward negative infinity): -4
# Ceiling (rounds up toward positive infinity): -3

4.7.5) Considerações Importantes com Arredondamento

A precisão de ponto flutuante pode causar surpresas:

Devido à forma como computadores representam números de ponto flutuante (que veremos na seção 4.10), o arredondamento nem sempre produz exatamente o resultado que você esperaria:

python
# rounding_surprises.py
# Sometimes rounding doesn't give the exact decimal you expect
value = 2.675
rounded = round(value, 2)
print(rounded)  # Output: 2.67 (not 2.68 as you might expect)
 
# This happens because 2.675 can't be represented exactly in binary floating-point
# The actual stored value is slightly less than 2.675

Para a maioria dos propósitos práticos, isso não é um problema. Mas, se você estiver trabalhando com cálculos financeiros em que a aritmética decimal exata é crucial, talvez você precise do módulo decimal do Python (que não cobriremos neste livro, mas vale saber que existe).

Arredondar para exibição vs arredondar para cálculo:

Muitas vezes você quer arredondar apenas para exibir, mantendo a precisão total para cálculos:

python
# rounding_for_display.py
price1 = 19.99
price2 = 15.49
tax_rate = 0.08
 
# Calculate with full precision
subtotal = price1 + price2
tax = subtotal * tax_rate
total = subtotal + tax
 
# Round only for display
print(f"Subtotal: ${round(subtotal, 2)}")  # Output: Subtotal: $35.48
print(f"Tax: ${round(tax, 2)}")            # Output: Tax: $2.84
print(f"Total: ${round(total, 2)}")        # Output: Total: $38.32
 
# The variables still contain full precision
print(f"Total (full precision): ${total}")
# Output: Total (full precision): $38.3184

A função round() é uma das funções embutidas mais usadas em Python. Use-a sempre que precisar apresentar resultados numéricos em um formato legível ou quando precisar limitar a precisão para um propósito específico.

4.8) Padrões Numéricos Comuns (contadores, totais, médias)

Agora que você entende as operações numéricas e funções do Python, vamos explorar padrões comuns que você vai usar repetidamente em programas reais. Esses padrões formam os blocos básicos para processar dados numéricos.

4.8.1) Contadores: Mantendo o Controle de Quantidades

Um contador é uma variável que acompanha quantas vezes algo acontece. Você a inicializa com zero e a incrementa cada vez que um evento ocorre:

python
# basic_counter.py
# Count how many numbers we've processed
count = 0  # Initialize counter
 
# Process first number
count += 1
print(f"Processed {count} number(s)")  # Output: Processed 1 number(s)
 
# Process second number
count += 1
print(f"Processed {count} number(s)")  # Output: Processed 2 number(s)
 
# Process third number
count += 1
print(f"Processed {count} number(s)")  # Output: Processed 3 number(s)

Em programas reais, você normalmente usaria contadores dentro de loops (que veremos nos Capítulos 10 e 11). Por enquanto, entenda o padrão: começar em zero, somar um a cada vez.

Contadores podem acompanhar diferentes tipos de eventos:

python
# multiple_counters.py
# Track different categories
even_count = 0
odd_count = 0
 
# Check number 1
number = 4
if number % 2 == 0:
    even_count += 1
else:
    odd_count += 1
 
# Check number 2
number = 7
if number % 2 == 0:
    even_count += 1
else:
    odd_count += 1
 
# Check number 3
number = 10
if number % 2 == 0:
    even_count += 1
else:
    odd_count += 1
 
print(f"Even numbers: {even_count}")  # Output: Even numbers: 2
print(f"Odd numbers: {odd_count}")    # Output: Odd numbers: 1

4.8.2) Acumuladores: Construindo Totais

Um acumulador é uma variável que coleta um total corrente. Como um contador, você o inicializa com zero, mas em vez de somar um a cada vez, você soma quantias variadas:

python
# basic_accumulator.py
# Calculate total sales
total_sales = 0  # Initialize accumulator
 
# First sale
sale = 19.99
total_sales += sale
print(f"Total so far: ${total_sales}")  # Output: Total so far: $19.99
 
# Second sale
sale = 34.50
total_sales += sale
print(f"Total so far: ${total_sales}")  # Output: Total so far: $54.49
 
# Third sale
sale = 12.00
total_sales += sale
print(f"Total so far: ${total_sales}")  # Output: Total so far: $66.49

Acumuladores são fundamentais para processamento de dados. Eles permitem agregar valores à medida que você os processa:

python
# multiple_accumulators.py
# Track both total and count to calculate average later
total_score = 0
count = 0
 
# Process score 1
score = 85
total_score += score
count += 1
 
# Process score 2
score = 92
total_score += score
count += 1
 
# Process score 3
score = 78
total_score += score
count += 1
 
print(f"Total score: {total_score}")  # Output: Total score: 255
print(f"Number of scores: {count}")   # Output: Number of scores: 3

4.8.3) Calculando Médias

Uma média combina os padrões de contador e acumulador: você precisa tanto de um total (acumulador) quanto de uma contagem (contador), e depois dividir:

python
# calculating_average.py
# Calculate average of test scores
total_score = 0
count = 0
 
# Add scores
total_score += 85
count += 1
 
total_score += 92
count += 1
 
total_score += 78
count += 1
 
total_score += 88
count += 1
 
# Calculate average
average = total_score / count
print(f"Average score: {average}")  # Output: Average score: 85.75
 
# Often you'll want to round the average
average_rounded = round(average, 2)
print(f"Average (rounded): {average_rounded}")  # Output: Average (rounded): 85.75

Importante: Sempre verifique divisão por zero:

Ao calcular médias, você precisa garantir que a contagem não seja zero:

python
# safe_average.py
total_score = 0
count = 0
 
# If no scores were added, count is still 0
# Dividing by zero causes an error!
 
if count > 0:
    average = total_score / count
    print(f"Average: {average}")
else:
    print("No scores to average")
    # Output: No scores to average

Vamos aprender mais sobre como lidar com condições assim no Capítulo 8.

4.8.4) Encontrando Máximo e Mínimo Correntes

Às vezes você precisa acompanhar o maior ou o menor valor visto até agora. Você pode usar as funções max() e min() para implementar isso:

python
# running_max_min_simplified.py
# Track highest and lowest using max() and min()
 
highest_temp = 72
lowest_temp = 72
 
# Update with new temperature
current_temp = 85
highest_temp = max(highest_temp, current_temp)
lowest_temp = min(lowest_temp, current_temp)
 
# Update with another temperature
current_temp = 68
highest_temp = max(highest_temp, current_temp)
lowest_temp = min(lowest_temp, current_temp)
 
print(f"High: {highest_temp}, Low: {lowest_temp}")
# Output: High: 85, Low: 68

4.9) Operadores Bit a Bit para Inteiros: &, |, ^, <<, >> (Visão Geral Rápida)

Operadores bit a bit trabalham nos bits individuais (dígitos binários) de inteiros. Embora você não use esses operadores tão frequentemente quanto operadores aritméticos na programação do dia a dia, eles são importantes para certas tarefas, como trabalhar com flags, permissões, manipulação de dados em baixo nível e otimização de desempenho.

Esta seção fornece uma visão geral rápida. Entender operações bit a bit exige entender representação binária, que é um tema mais profundo do que podemos explorar totalmente neste capítulo introdutório.

4.9.1) Entendendo Representação Binária (Introdução Rápida)

Computadores armazenam inteiros como sequências de bits (0s e 1s). Por exemplo:

  • Decimal 5 é binário 101 (1×4 + 0×2 + 1×1)
  • Decimal 12 é binário 1100 (1×8 + 1×4 + 0×2 + 0×1)

A função bin() do Python mostra a representação binária de um inteiro:

python
# binary_representation.py
# See binary representation of integers
print(bin(5))   # Output: 0b101
print(bin(12))  # Output: 0b1100
print(bin(255)) # Output: 0b11111111
 
# The 0b prefix indicates binary notation

4.9.2) AND Bit a Bit (&)

O operador & realiza um AND bit a bit: cada bit no resultado é 1 apenas se os bits correspondentes em ambos os operandos forem 1:

python
# bitwise_and.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 & 10
print(result)  # Output: 8
print(bin(result))  # Output: 0b1000
 
# How it works:
#   1100  (12)
# & 1010  (10)
# ------
#   1000  (8)

AND bit a bit é frequentemente usado para verificar se bits específicos estão ligados (testar flags):

python
# checking_flags.py
# File permissions example (simplified)
READ = 4    # 100 in binary
WRITE = 2   # 010 in binary
EXECUTE = 1 # 001 in binary
 
permissions = 6  # 110 in binary (READ + WRITE)
 
# Check if READ permission is set
has_read = (permissions & READ) != 0
print(f"Has read: {has_read}")  # Output: Has read: True
 
# Check if EXECUTE permission is set
has_execute = (permissions & EXECUTE) != 0
print(f"Has execute: {has_execute}")  # Output: Has execute: False

4.9.3) OR Bit a Bit (|)

O operador | realiza um OR bit a bit: cada bit no resultado é 1 se o bit correspondente em qualquer operando for 1:

python
# bitwise_or.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 | 10
print(result)  # Output: 14
print(bin(result))  # Output: 0b1110
 
# How it works:
#   1100  (12)
# | 1010  (10)
# ------
#   1110  (14)

OR bit a bit é usado para combinar flags:

python
# combining_flags.py
READ = 4    # 100 in binary
WRITE = 2   # 010 in binary
EXECUTE = 1 # 001 in binary
 
# Grant READ and WRITE permissions
permissions = READ | WRITE
print(f"Permissions: {permissions}")  # Output: Permissions: 6
print(bin(permissions))  # Output: 0b110
 
# Add EXECUTE permission
permissions = permissions | EXECUTE
print(f"Permissions: {permissions}")  # Output: Permissions: 7
print(bin(permissions))  # Output: 0b111

4.9.4) XOR Bit a Bit (^)

O operador ^ realiza um XOR bit a bit (OU exclusivo): cada bit no resultado é 1 se os bits correspondentes nos operandos forem diferentes:

python
# bitwise_xor.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 ^ 10
print(result)  # Output: 6
print(bin(result))  # Output: 0b110
 
# How it works:
#   1100  (12)
# ^ 1010  (10)
# ------
#   0110  (6)

XOR tem propriedades interessantes, como alternar bits ou trocar valores:

python
# xor_properties.py
# XOR with itself gives 0
result = 5 ^ 5
print(result)  # Output: 0
 
# XOR with 0 gives the original number
result = 5 ^ 0
print(result)  # Output: 5
 
# XOR is its own inverse (useful for simple encryption)
original = 42
key = 123
encrypted = original ^ key
decrypted = encrypted ^ key
print(f"Original: {original}, Encrypted: {encrypted}, Decrypted: {decrypted}")
# Output: Original: 42, Encrypted: 81, Decrypted: 42

4.9.5) Deslocamento à Esquerda (<<) e à Direita (>>)

O operador << desloca bits para a esquerda, e >> desloca bits para a direita:

python
# bit_shifting.py
# Left shift: multiply by powers of 2
result = 5 << 1  # Shift left by 1 bit
print(result)  # Output: 10
# 5 is 101 in binary, shifted left becomes 1010 (10)
 
result = 5 << 2  # Shift left by 2 bits
print(result)  # Output: 20
# 5 is 101 in binary, shifted left becomes 10100 (20)
 
# Right shift: divide by powers of 2 (floor division)
result = 20 >> 1  # Shift right by 1 bit
print(result)  # Output: 10
# 20 is 10100 in binary, shifted right becomes 1010 (10)
 
result = 20 >> 2  # Shift right by 2 bits
print(result)  # Output: 5
# 20 is 10100 in binary, shifted right becomes 101 (5)

Deslocar para a esquerda por n bits multiplica por 2^n, e deslocar para a direita divide por 2^n (divisão inteira):

python
# shift_as_multiplication.py
# Left shift multiplies by powers of 2
print(3 << 1)  # Output: 6 (3 * 2^1 = 3 * 2)
print(3 << 2)  # Output: 12 (3 * 2^2 = 3 * 4)
print(3 << 3)  # Output: 24 (3 * 2^3 = 3 * 8)
 
# Right shift divides by powers of 2
print(24 >> 1)  # Output: 12 (24 // 2^1 = 24 // 2)
print(24 >> 2)  # Output: 6 (24 // 2^2 = 24 // 4)
print(24 >> 3)  # Output: 3 (24 // 2^3 = 24 // 8)

4.9.6) Quando Usar Operadores Bit a Bit

Operadores bit a bit são úteis em cenários específicos:

  • Trabalhar com flags e permissões binárias (como permissões de arquivos em Unix/Linux)
  • Manipulação de dados em baixo nível (empacotar múltiplos valores em um único inteiro)
  • Programação de rede (manipulando endereços IP, flags de protocolo)
  • Gráficos e programação de jogos (manipulação de cores, detecção de colisão)
  • Otimização de desempenho (deslocamento de bits é mais rápido que multiplicação/divisão por potências de 2)

Para a maioria das tarefas de programação do dia a dia, você não vai precisar de operadores bit a bit. Mas quando precisar — especialmente ao trabalhar com programação de sistemas, redes ou código crítico de desempenho — eles são ferramentas valiosas.

Nota: Esta visão geral cobre o básico. Operações bit a bit ficam mais complexas com números negativos (envolvendo representação em complemento de dois), o que está além do escopo desta introdução. Por enquanto, foque em entender que esses operadores existem e o que fazem em alto nível.

4.10) Precisão de Ponto Flutuante e Erros de Arredondamento (Explicação Simples)

Números de ponto flutuante em Python (e na maioria das linguagens de programação) podem representar uma enorme faixa de valores, de frações minúsculas a números enormes. No entanto, eles têm uma limitação que pode surpreender iniciantes: eles não conseguem representar todos os números decimais exatamente. Esta seção explica por que isso acontece e o que isso significa para seus programas.

4.10.1) Por Que Números de Ponto Flutuante Nem Sempre São Exatos

Computadores armazenam números de ponto flutuante em binário (base 2), não em decimal (base 10). Algumas frações decimais que parecem simples para nós não podem ser representadas exatamente em binário, assim como 1/3 não pode ser representado exatamente em decimal (0.333333... continua para sempre).

Aqui vai um exemplo surpreendente:

python
# floating_point_surprise.py
# This seems like it should be exactly 0.3
result = 0.1 + 0.2
print(result)  # Output: 0.30000000000000004 (not exactly 0.3!)
 
# Check if it equals 0.3
print(result == 0.3)  # Output: False

Isso não é um bug no Python — é uma limitação fundamental de como computadores representam números de ponto flutuante. O número decimal 0.1 não pode ser representado exatamente em ponto flutuante binário, assim como 1/3 não pode ser representado exatamente em decimal.

4.10.2) Entendendo o Problema de Representação

Vamos ver mais exemplos desse comportamento:

python
# more_precision_examples.py
# Simple decimal values that aren't exact in binary
print(0.1)  # Output: 0.1 (Python rounds for display)
print(repr(0.1))  # Output: 0.1 (still rounded)
 
# But the actual stored value has tiny errors
print(0.1 + 0.1 + 0.1)  # Output: 0.30000000000000004
 
# Multiplication can accumulate these errors
result = 0.1 * 3
print(result)  # Output: 0.30000000000000004
 
# Some numbers are exact (powers of 2)
print(0.5)  # Output: 0.5 (exact)
print(0.25)  # Output: 0.25 (exact)
print(0.125)  # Output: 0.125 (exact)

Números que são potências de 2 (como 0.5, 0.25, 0.125) ou somas de potências de 2 podem ser representados exatamente. Mas a maioria das frações decimais não pode.

4.10.3) Implicações Práticas

Para a maioria da programação do dia a dia, esses pequenos erros não importam. Mas há situações em que você precisa estar atento a eles:

Comparando números de ponto flutuante:

python
# comparing_floats.py
# Direct equality comparison can fail
a = 0.1 + 0.2
b = 0.3
 
print(a == b)  # Output: False (due to tiny difference)
 
# Better: check if they're close enough
difference = abs(a - b)
tolerance = 0.0001  # How close is "close enough"?
 
print(difference < tolerance)  # Output: True (they're close enough)
 
# Python 3.5+ provides math.isclose() for this (we'll see in section 4.11)

Acumular erros em cálculos repetidos:

python
# accumulated_errors.py
# Adding 0.1 ten times
total = 0.0
for i in range(10):
    total += 0.1
 
print(total)  # Output: 0.9999999999999999 (not exactly 1.0)
print(total == 1.0)  # Output: False
 
# The error accumulates with each addition

Cálculos financeiros:

python
# financial_calculations.py
# Money calculations can have surprising results
price = 0.10
quantity = 3
total = price * quantity
 
print(total)  # Output: 0.30000000000000004
 
# For financial calculations, consider rounding to cents
total_cents = round(total * 100)  # Convert to cents
total_dollars = total_cents / 100
print(total_dollars)  # Output: 0.3
 
# Or use Python's decimal module for exact decimal arithmetic
# (beyond the scope of this chapter, but worth knowing about)

4.10.4) Estratégias para Trabalhar com Números de Ponto Flutuante

Arredonde resultados para exibição:

python
# rounding_for_display.py
result = 0.1 + 0.2
print(f"Result: {round(result, 2)}")  # Output: Result: 0.3
 
# Or use formatted output (we'll learn more in Chapter 6)
print(f"Result: {result:.2f}")  # Output: Result: 0.30

Não compare floats por igualdade exata:

python
# safe_float_comparison.py
a = 0.1 + 0.2
b = 0.3
 
# Instead of: if a == b:
# Use: if they're close enough
if abs(a - b) < 0.0001:
    print("Close enough to equal")
# Output: Close enough to equal

Esteja ciente quando a precisão importar:

Para computação científica, cálculos financeiros ou qualquer domínio em que aritmética decimal exata seja crucial, talvez você precise de:

  • Módulo decimal do Python para aritmética decimal exata
  • Módulo fractions do Python para aritmética racional exata
  • Arredondamento cuidadoso em pontos apropriados dos seus cálculos

Esses módulos estão além do escopo deste capítulo, mas saber que eles existem é valioso.

4.10.5) Quando a Precisão de Ponto Flutuante Não Importa

Para a maioria das tarefas de programação do dia a dia, a precisão de ponto flutuante é mais do que suficiente:

python
# when_precision_is_fine.py
# Calculating area
length = 5.5
width = 3.2
area = length * width
print(f"Area: {area:.2f} square meters")  # Output: Area: 17.60 square meters
 
# Converting temperature
fahrenheit = 98.6
celsius = (fahrenheit - 32) * 5 / 9
print(f"Temperature: {celsius:.1f}°C")  # Output: Temperature: 37.0°C
 
# Calculating average
total = 456.78
count = 7
average = total / count
print(f"Average: {average:.2f}")  # Output: Average: 65.25

Quando você arredonda resultados para exibição ou quando pequenos erros são insignificantes em relação à precisão da sua medição, a aritmética de ponto flutuante funciona muito bem.

4.10.6) A Principal Conclusão

Números de ponto flutuante são aproximações de números reais. Para a maioria das tarefas de programação, eles são suficientemente precisos. Mas lembre-se:

  1. Não compare floats por igualdade exata — verifique se eles são "próximos o suficiente"
  2. Arredonde resultados ao exibir para evitar mostrar precisão sem sentido
  3. Fique atento à acumulação de erros em cálculos repetidos
  4. Para cálculos financeiros, considere arredondar para a precisão apropriada ou usar o módulo decimal

Entender essas limitações te ajuda a escrever programas mais robustos e evitar bugs surpreendentes. A boa notícia é que, para a grande maioria das tarefas de programação, a aritmética de ponto flutuante "simplesmente funciona" se você seguir essas diretrizes simples.

4.11) (Opcional) Funções Matemáticas Básicas com o Módulo math (sqrt, floor, ceil, pi)

Os operadores e funções embutidas do Python cobrem a aritmética básica, mas para operações matemáticas mais avançadas, o Python fornece o módulo math. Um módulo é uma coleção de funções e valores relacionados que você pode usar em seus programas. Vamos aprender muito mais sobre módulos no Capítulo 22, mas por enquanto, vamos apresentar o básico que você precisa para usar o módulo math.

4.11.1) Importando o Módulo math

Para usar o módulo math, você precisa importá-lo no começo do seu programa:

python
# importing_math.py
import math
 
# Now you can use functions from the math module
# by writing math.function_name()

Quando você importa um módulo, ganha acesso a todas as suas funções e valores. Você os usa escrevendo o nome do módulo, um ponto e então o nome da função ou valor.

4.11.2) Constantes Matemáticas: pi e e

O módulo math fornece valores precisos para constantes matemáticas importantes:

python
# math_constants.py
import math
 
# Pi (π): ratio of circle's circumference to diameter
print(math.pi)  # Output: 3.141592653589793
 
# Euler's number (e): base of natural logarithms
print(math.e)  # Output: 2.718281828459045
 
# Using pi to calculate circle properties
radius = 5
circumference = 2 * math.pi * radius
area = math.pi * radius ** 2
 
print(f"Circumference: {circumference:.2f}")  # Output: Circumference: 31.42
print(f"Area: {area:.2f}")  # Output: Area: 78.54

4.11.3) Raiz Quadrada com sqrt()

A função sqrt() calcula a raiz quadrada de um número:

python
# square_root.py
import math
 
# Square root of perfect squares
result = math.sqrt(16)
print(result)  # Output: 4.0
 
result = math.sqrt(25)
print(result)  # Output: 5.0
 
# Square root of non-perfect squares
result = math.sqrt(2)
print(result)  # Output: 1.4142135623730951
 
# Using sqrt in calculations
# Distance formula: sqrt((x2-x1)^2 + (y2-y1)^2)
x1, y1 = 3, 4
x2, y2 = 6, 8
 
distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
print(f"Distance: {distance}")  # Output: Distance: 5.0

Note que sqrt() retorna um float, mesmo para quadrados perfeitos. Além disso, você não pode tirar a raiz quadrada de números negativos com sqrt() (isso vai gerar um erro).

4.11.4) Floor e Ceiling com floor() e ceil()

A função floor() arredonda para baixo até o inteiro mais próximo (em direção a menos infinito), e ceil() arredonda para cima (em direção a mais infinito):

python
# floor_and_ceil.py
import math
 
# Floor: rounds down
result = math.floor(3.7)
print(result)  # Output: 3
 
result = math.floor(3.2)
print(result)  # Output: 3
 
# Ceiling: rounds up
result = math.ceil(3.7)
print(result)  # Output: 4
 
result = math.ceil(3.2)
print(result)  # Output: 4
 
# With negative numbers
result = math.floor(-3.7)
print(result)  # Output: -4 (rounds toward negative infinity)
 
result = math.ceil(-3.7)
print(result)  # Output: -3 (rounds toward positive infinity)

Essas funções são úteis quando você precisa garantir que um valor esteja dentro de um certo intervalo ou quando precisa de resultados inteiros com um comportamento específico de arredondamento:

python
# practical_floor_ceil.py
import math
 
# How many boxes needed to pack all items?
items = 47
items_per_box = 12
 
# Use ceil to round up (need a full box even if not completely filled)
boxes_needed = math.ceil(items / items_per_box)
print(f"Boxes needed: {boxes_needed}")  # Output: Boxes needed: 4
 
# How many complete pages for a document?
total_lines = 250
lines_per_page = 30
 
# Use floor to count only complete pages
complete_pages = math.floor(total_lines / lines_per_page)
print(f"Complete pages: {complete_pages}")  # Output: Complete pages: 8

4.11.5) Comparando floor(), ceil(), round() e int()

É útil entender como essas diferentes abordagens de arredondamento diferem:

python
# comparing_rounding_methods.py
import math
 
value = 3.7
 
print(f"Original: {value}")
print(f"floor(): {math.floor(value)}")  # Output: floor(): 3
print(f"ceil(): {math.ceil(value)}")    # Output: ceil(): 4
print(f"round(): {round(value)}")       # Output: round(): 4
print(f"int(): {int(value)}")           # Output: int(): 3
 
# With negative numbers, the differences are more apparent
value = -3.7
 
print(f"\nOriginal: {value}")
print(f"floor(): {math.floor(value)}")  # Output: floor(): -4
print(f"ceil(): {math.ceil(value)}")    # Output: ceil(): -3
print(f"round(): {round(value)}")       # Output: round(): -4
print(f"int(): {int(value)}")           # Output: int(): -3

Aqui está uma representação visual de como essas funções se comportam:

Value: 3.7

floor: 3
rounds down

ceil: 4
rounds up

round: 4
rounds to nearest

int: 3
truncates toward zero

Value: -3.7

floor: -4
rounds down toward -∞

ceil: -3
rounds up toward +∞

round: -4
rounds to nearest

int: -3
truncates toward zero

4.11.6) Outras Funções Úteis do Módulo math

O módulo math contém muitas outras funções. Aqui vão mais algumas comumente úteis:

python
# other_math_functions.py
import math
 
# Absolute value (also available as built-in abs())
result = math.fabs(-5.5)
print(result)  # Output: 5.5
 
# Power function (also available as ** operator)
result = math.pow(2, 3)
print(result)  # Output: 8.0
 
# Trigonometric functions (angles in radians)
result = math.sin(math.pi / 2)  # sin(90 degrees)
print(result)  # Output: 1.0
 
result = math.cos(0)  # cos(0 degrees)
print(result)  # Output: 1.0
 
# Logarithms
result = math.log(math.e)  # Natural log (base e)
print(result)  # Output: 1.0
 
result = math.log10(100)  # Log base 10
print(result)  # Output: 2.0
 
# Check if a float is close to another (Python 3.5+)
a = 0.1 + 0.2
b = 0.3
result = math.isclose(a, b)
print(result)  # Output: True

A função isclose() é particularmente útil para comparar números de ponto flutuante (como discutimos na seção 4.10):

python
# isclose_example.py
import math
 
# Instead of direct equality comparison
a = 0.1 + 0.2
b = 0.3
 
# Don't do this:
# if a == b:  # This would be False
 
# Do this instead:
if math.isclose(a, b):
    print("Values are close enough")
# Output: Values are close enough
 
# You can specify the tolerance
if math.isclose(a, b, rel_tol=1e-9):  # Very strict tolerance
    print("Values are very close")
# Output: Values are very close

4.11.7) Quando Usar o Módulo math

Use o módulo math quando você precisar de:

  • Constantes matemáticas (π, e)
  • Raiz quadrada e outras raízes
  • Funções trigonométricas (sin, cos, tan)
  • Funções logarítmicas e exponenciais
  • Controle preciso de arredondamento (floor, ceil)
  • Comparação de ponto flutuante (isclose)

Para aritmética básica (+, -, *, /, //, %, **), use os operadores embutidos do Python. Para operações matemáticas mais avançadas, importe e use o módulo math.

O módulo math faz parte da biblioteca padrão do Python, o que significa que ele está sempre disponível — você só precisa importá-lo. Vamos explorar mais módulos e aprender sobre o sistema de importação em detalhes no Capítulo 22.


Neste capítulo, você aprendeu a trabalhar com números em Python: realizando operações aritméticas, entendendo diferentes tipos de divisão, usando precedência de operadores, misturando inteiros e floats, empregando funções numéricas embutidas, reconhecendo padrões numéricos comuns, trabalhando com operadores bit a bit, entendendo precisão de ponto flutuante e usando o módulo math para operações avançadas.

Essas operações numéricas formam a base para incontáveis tarefas de programação, de cálculos simples a análises de dados complexas. À medida que você continua aprendendo Python, vai usar essas operações constantemente, muitas vezes em combinação com as estruturas de controle de fluxo e coleções de dados que verá nos próximos capítulos.

Pratique essas operações até que se tornem naturais. Tente escrever pequenos programas que calculam quantidades do mundo real: converta temperaturas, calcule áreas e volumes, processe dados financeiros ou analise medições. Quanto mais você praticar, mais à vontade você ficará com os recursos numéricos do Python.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai