Python & AI Tutorials Logo
Python プログラミング

4. 数値の扱い方

第3章では、変数の作成方法と、Python の基本的な数値型である整数と浮動小数点数の扱い方を学びました。ここからは、それらの数値を実際に使っていきます。この章では、計算の実行方法、演算の組み合わせ方、そして数値データを扱うための Python の組み込みツールの使い方を学びます。

数値はプログラミングの基本です。合計を計算したり、計測値を処理したり、在庫を管理したり、データを分析したりするときには、算術演算を行う必要があります。Python では数値演算を簡単かつ直感的に行えますが、とても重要な細かい点もあります。特に、異なる種類の割り算の動き、演算子どうしの関係、そして浮動小数点数の挙動について理解しておく必要があります。

この章の終わりには、計算の実行、演算子の振る舞いの理解、そして Python の数値関数を使って現実世界の問題を解決できるようになっているはずです。

4.1) 基本の算術: 加算・減算・乗算

まず最も基本的な算術演算から始めます。Python では、よく見慣れた記号を使って基本的な数学演算ができ、これらの演算は日常の算数の感覚どおりに動作します。

4.1.1) + 演算子による加算

+ 演算子は2つの数値を足します。これは整数にも浮動小数点数にも使えます。

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

加算は単純ですが、重要なポイントが1つあります。整数と浮動小数点数を足すと、Python は結果を自動的に浮動小数点数へ変換して、小数の精度を保ちます。

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

この自動変換は、浮動小数点数が整数と小数の両方を表現できるのに対して、整数は小数を表現できないために行われます。Python は情報が失われない型を選びます。

4.1.2) - 演算子による減算

- 演算子は、2つ目の数値を1つ目の数値から引きます。

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

加算と同様に、異なる型を混ぜるときには、減算でも整数が浮動小数点数に昇格します。

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

4.1.3) * 演算子による乗算

* 演算子は2つの数値を掛け算します。

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

乗算も同じ型変換ルールに従います。整数と浮動小数点数を掛けると、結果は浮動小数点数になります。

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'>

最後の例では、4 × 2.0 は数学的には 8 ですが、片方のオペランドが浮動小数点数だったため、Python は結果を 8.0 として表現していることに注意してください。結果の型は、数学的な結果が整数かどうかではなく、入力の型によって決まります。

4.1.4) 基本算術演算の実践例

これらの演算が現実的な場面でどのように一緒に使われるか見てみましょう。

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

これらの基本演算は、より複雑な計算の土台になります。特に、Python がどのように型変換を行うかを理解しておくことで、プログラムが計算を行うときに思わぬ結果に驚かされることを避けられます。

4.2) 割り算・切り捨て割り算・余り: /, //, %

Python における割り算は、加算・減算・乗算よりも少し複雑です。Python には、用途の異なる3種類の割り算関連の演算子があります。それぞれをいつ使うべきかを理解することは、正しいプログラムを書くためにとても重要です。

4.2.1) / による通常の割り算

/ 演算子は「真の割り算 (true division)」を行います。2つの整数同士を割っても、結果は常に浮動小数点数になります。

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

ここでの重要なポイントは、/常に 浮動小数点数を返すことです。オペランドが両方とも整数で、数学的な結果が整数になる場合でも同じです。これは設計上の仕様で、Python は割り算において一貫した動作を保ち、小数の精度を保持しようとします。

この挙動は、2つの整数を割ると整数が返る言語があるのとは異なります。Python 3 では、整数の結果が欲しい場合は、切り捨て割り算(この後で説明します)を使うか、明示的に変換する必要があります。

4.2.2) // による切り捨て割り算 (floor division)

// 演算子は「切り捨て割り算 (floor division)」を行います。割り算をしてから、結果を小さい方の整数に切り捨てます。オペランドが両方とも整数のとき、結果は整数です。どちらか一方でも浮動小数点数なら、結果は浮動小数点数ですが、依然として切り捨てられます。

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'>

「切り捨て」は単に小数部分を削るのではなく、負の無限大の方向に丸めることを意味します。これは負の数で重要になります。

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 ...

切り捨て割り算は、アイテムをグループに分けるときや、ある量に完全に含まれる単位数を求めるときに役立ちます。

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) % による余り (剰余, modulo)

% 演算子(「モジュロ」「モッド」などと呼びます)は、割り算の余りを返します。「割ったあとにどれだけ余るか?」に答えてくれる演算子です。

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

モジュロ演算子は、よくあるプログラミングパターンで非常に便利です。

数が偶数か奇数かの判定:

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

余ったアイテムの数を求める:

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

値を一定の範囲内で循環させる(時計算のようなもの):

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) //, %, / の関係

切り捨て割り算とモジュロは密接に関連しています。この2つを組み合わせると、割り算の結果を完全に表現できます。

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

この関係は常に成り立ちます。0 でない任意の b について、任意の a に対して a == (a // b) * b + (a % b) です。

4.2.5) 適切な割り算演算子の選び方

どの演算子を使うべきかの早見表です。

  • 小数を含む「正確な数学的な結果」が欲しいときは / を使う(多くの場合の計算で最もよく使います)
  • いくつの完全なグループに分けられるかを数えたいとき(ダース、時間、ページ数など)は // を使う
  • 余りや端数を知りたいとき(偶数/奇数判定、値の循環、余りの計算など)は % を使う
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) 複合代入演算子

プログラミングでは、変数の現在の値に演算を行って更新する場面が頻繁に出てきます。たとえば、合計値を増やしたり、残高から差し引いたり、値を係数で掛けたりするような場合です。Python では、これを簡潔に書けるように複合代入演算子を用意しています。

4.3.1) 複合代入演算子とは

複合代入演算子は、算術演算と代入を1つにまとめたものです。次のように書く代わりに:

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

次のように書けます。

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

どちらもまったく同じことをしますが、複合代入の方が簡潔で、「count を 5 増やす」という意図がよりはっきり表現されています。

4.3.2) すべての複合代入演算子

Python には、すべての算術演算に対応する複合代入演算子があります。

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) 複合代入の代表的な使いどころ

複合代入演算子は、よくあるプログラミングパターンで特に威力を発揮します。

合計値の蓄積:

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

回数のカウント:

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

残高の更新:

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

繰り返しの演算の適用:

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) 複合代入に関する重要なポイント

複合代入は、イミュータブルな型では新しいオブジェクトを作る:

第3章で学んだように、数値はイミュータブル(変更不可能)です。つまり、数の値そのものを変更するのではなく、新しい数値オブジェクトを作るだけです。x += 5 と書くと、Python は新しい数値オブジェクトを作り、それを x に代入します。古い数値オブジェクトは破棄されます(Python のオブジェクトモデルについては、第17章で詳しく扱います)。

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

今のところは、x += 5x = x + 5 と等価であり、便利な省略記法にすぎないと理解しておけば十分です。

変数が存在する前には複合代入を使えない:

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

型変換のルールは同じ:

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'>

結果の型は通常の演算子と同じルールに従います。整数と浮動小数点数を混ぜると、結果は浮動小数点数になります。

4.3.5) 複合代入を使うべき場面

変数の現在の値を元にして更新するときは、複合代入演算子を使いましょう。そうすることでコードは次のようになります。

  • より簡潔: x += 5x = x + 5 より短い
  • より読みやすい: 意図がすぐに伝わる
  • よりミスが少ない: 代入時の変数名の打ち間違いを減らせる

次の2つを比べてみましょう。

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

複合代入を使った方が分かりやすく、タイプミスの危険も減ります。複合代入演算子は小さな機能ですが、実際の Python コードでは非常に頻繁に使われます。

4.4) 演算子の優先順位とかっこ

1つの式の中で複数の演算を組み合わせるとき、Python はどの順番で計算するかを決めるルールが必要です。たとえば 2 + 3 * 4(2 + 3) * 4 = 20 と計算すべきでしょうか、それとも 2 + (3 * 4) = 14 と計算すべきでしょうか。この答えは、どの演算を先に行うかを定める「演算子の優先順位 (operator precedence)」によって決まります。

4.4.1) 演算子の優先順位の理解

Python は、数学で学んだのと同じ優先順位ルールに従います。掛け算と割り算が、足し算と引き算より先に行われます。しばしば PEMDAS(Parentheses, Exponents, Multiplication/Division, Addition/Subtraction)という略語で覚えられます(べき乗については 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

これまでに学んだ演算子についての優先順位は、高いものから低いものへ次のようになります。

  1. かっこ () — 最優先で、常に最初に評価されます
  2. べき乗 ** —(4.6 節で扱います)
  3. 乗算・割り算・切り捨て割り算・モジュロ *, /, //, % — 同じレベルで、左から右へ評価
  4. 加算・減算 +, - — 同じレベルで、左から右へ評価

優先順位の動きをさらに見てみましょう。

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) かっこで順序を制御する

かっこはデフォルトの優先順位を上書きします。かっこの内側の演算は、使われている演算子に関わらず、常に最初に行われます。

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

かっこは入れ子にすることもでき、複雑な式を作れます。Python は一番内側のかっこから外側へ向かって順に評価します。

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) 同じ優先順位の演算子が並んだとき

同じ優先順位レベルの演算子が式の中に複数ある場合、Python はそれらを左から右へ評価します(これを「左結合 (left associativity)」と呼びます)。

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) 優先順位の実践例

優先順位の理解が重要になる、現実的な例をいくつか見てみましょう。

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) 演算子の優先順位に関するベストプラクティス

厳密には不要でも、意図を明確にするためにかっこを使う:

かっこを使うことで、結果は変わらなくても意図をより明確にできる場合があります。

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

複雑な式は分割する:

式が複雑になりすぎたら、複数行に分けた方がよいことが多いです。

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

複数のステップに分けた方が読みやすく、デバッグや検証もしやすくなります。短さのために分かりやすさを犠牲にしないようにしましょう。

迷ったらかっこを使う:

優先順位に自信がないときは、かっこを足しておきましょう。Python インタプリタは気にしませんし、後でコードを読む自分や他のプログラマにとっても親切です。

演算子の優先順位を理解することで、正しい式を書き、他人のコードも読みやすくなります。複数の演算を組み合わせるときは、評価順を意識し、かっこを使って意図をはっきり示すことが大切です。

4.5) 式の中で整数と浮動小数点数を混在させる

これまで、整数と浮動小数点数を混ぜた簡単な演算で、Python が自動的に処理してくれることを見てきました。ここでは、この挙動をもう少し体系的に見て、数値式における型変換のルールを理解しましょう。

4.5.1) 型昇格 (type promotion) のルール

Python が整数と浮動小数点数を両方含む算術演算を行うとき、整数を自動的に浮動小数点数へ変換(「昇格」)してから演算を実行します。結果は常に浮動小数点数になります。

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'>

Python がこうするのは、浮動小数点数が整数も小数も表現できるのに対して、整数は小数を表現できないからです。浮動小数点数へ変換することで、情報を失わずに済みます。

Python が結果の型を決める流れを図にすると次のようになります。

Yes

No

Yes

No

Operation with
int and float

Any operand
is float?

Result is float

Division
operator /?

Result is int

4.5.2) 複雑な式における型昇格

複数の演算を含む式で型が混在している場合、Python は各ステップでこの型昇格ルールを適用します。

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)

いったん式のどこかで浮動小数点数が生まれると、その結果を使う以降の演算もすべて浮動小数点数になります。

4.5.3) 特別な場合: 通常の割り算 / は常に浮動小数点数

4.2 節で見たように、/ 演算子は2つの整数同士の割り算でも常に浮動小数点数を返します。

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'>

割り算の結果を整数で欲しい場合は、代わりに //(切り捨て割り算)を使います。ただし、これは切り捨てることに注意してください。

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) 型混在の実務的な影響

型昇格を理解しておくと、結果の型を予測・制御しやすくなります。

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) 整数結果が重要になるとき

カウントやインデックス、その他の離散的な値として整数結果が必須な場合があります。そのようなときに整数を確実に得る方法を見てみましょう。

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

// は「負の無限大方向への丸め(切り捨て)」を行い、int() は 0 方向への切り捨てを行うという違いがあります。正の数では結果は同じですが、負の数では異なります。

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) 思いがけない浮動小数点結果を避ける

整数を期待していたのに浮動小数点数になって驚くことがあります。たいていは、割り算か型の混在が原因です。

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)

ポイントは、Python の型昇格ルールは精度を保ち、データ損失を避けるように設計されているということです。整数と浮動小数点数を混ぜるとき、あるいは通常の割り算を使うときは、結果が浮動小数点数になると考えてください。整数が必要な場合は、切り捨て割り算や明示的な変換を使いますが、そのときの丸め方の違いには注意しましょう。

4.6) 便利な数値の組み込み関数: abs(), min(), max(), pow()

Python には、よく使う数値演算を行うための組み込み関数がいくつか用意されています。これらの関数は整数にも浮動小数点数にも使え、日常的なプログラミングに欠かせないツールです。ここでは、最もよく使われるものを見ていきます。

4.6.1) abs() による絶対値

abs() 関数は、数値の絶対値(符号を無視した 0 からの距離)を返します。

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

絶対値関数は、大きさだけが重要で向き(符号)はどうでもよい場合に役立ちます。

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) min()max() による最小値・最大値の取得

min() 関数は引数の中で最も小さい値を返し、max() は最も大きい値を返します。

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

これらの関数は、データの中から極値を見つけるときに非常に役立ちます。

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

このように max()min() を組み合わせて値を範囲内に収めるパターン(クランプ)は、値を特定の範囲に制限したいときによく使われます。

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) pow() によるべき乗

pow() 関数は、数値のべき乗を計算します。Python にはべき乗用の演算子 ** もあり、こちらの方が一般的によく使われますが、pow() には追加機能もあります。

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

pow() 関数は、第3引数として剰余を取るモジュラべき乗を指定できます(暗号や数論で有用ですが、基礎的な算術の範囲を越えた話題です)。

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

日常的な用途では、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) 組み込み関数の組み合わせ

これらの関数は組み合わせて使うことで、よくある問題を解くのに役立ちます。

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

これらの組み込み関数は、Python プログラミングにおける基本的なツールです。効率的でよくテストされており、異なる数値型に対して一貫した動作をします。こうした典型的な処理が必要なときは、自分で実装せず、これらの関数を積極的に使いましょう。

4.7) round() による丸め

浮動小数点数を扱うとき、表示・計算・保存などの目的で、小数点以下の桁数を指定して丸めたい場面がよくあります。Python の round() 関数は、そのための機能を提供します。

4.7.1) round() による基本的な丸め

round() 関数は、数値を最も近い整数に丸めます。

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

2.5 や 3.5 のように、ちょうど2つの整数の中間にある値を丸めるときの挙動に注目してください。Python は「偶数への丸め (round half to even)」または「バンカーズラウンディング (banker's rounding)」という方式を採用しており、最も近い偶数へ丸めます。これは、繰り返し丸めを行ったときの偏りを減らすためです。日常的なプログラミングでは、この細かい挙動が問題になることはあまりありませんが、知っておくとよいでしょう。

4.7.2) 特定の小数桁への丸め

round() 関数は、保持したい小数桁数を指定する第2引数を受け取れます。

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)

小数桁数を指定した場合、round() は 0 桁に丸めたとしても浮動小数点数を返します。第2引数を省略した場合は整数を返します。

4.7.3) 丸めの実践的な用途

丸めは、お金の金額、計測値、その他過度な精度が不要または紛らわしい値を表示するときに不可欠です。

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) 丸め・切り捨て・床/天井の違い

丸めは、小数部を単純に切り捨てる(トランケート)ことや、床/天井関数とは異なることを理解しておくことが重要です。

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)

負の数では、この違いがより顕著になります。

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) 丸めに関する重要な注意点

浮動小数点の精度による意外な結果:

コンピュータが浮動小数点数を表現する方法(4.10 節で説明します)のために、丸めの結果が期待と完全に一致しない場合があります。

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

多くの実用的な場面では問題になりませんが、金額計算など、厳密な10進小数計算が必要な場合には Python の decimal モジュールを使う必要が出てきます(この本では扱いませんが、そのようなモジュールがあることは覚えておく価値があります)。

表示用の丸めと計算用の値の違い:

多くの場合、計算では完全な精度を維持しつつ、表示するときだけ丸めたいことがよくあります。

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

round() は Python でもっとも頻繁に使われる組み込み関数の1つです。数値結果を読みやすい形で表示したいときや、特定の目的で精度を制限したいときに活用しましょう。

4.8) よくある数値パターン(カウンタ・合計・平均)

ここまでで Python の数値演算と関数を学んできました。次に、実際のプログラムで繰り返し使うことになる、よくあるパターンを見ていきます。これらのパターンは、数値データ処理のビルディングブロックとなります。

4.8.1) カウンタ: 回数を数える

カウンタは、何回何かが起きたかを記録する変数です。0 で初期化し、イベントが発生するたびに 1 ずつ増やします。

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)

実際のプログラムでは、カウンタは通常ループ(10章と11章で学びます)の中で使われます。今のところは、「0 から始めて毎回 1 足していく」というパターンを理解しておけば十分です。

カウンタは、さまざまな種類のイベントを追跡するのにも使えます。

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) アキュムレータ: 合計の蓄積

アキュムレータ(蓄積用変数)は、合計値を保持する変数です。カウンタと同じように 0 で初期化しますが、毎回 1 足すのではなく、状況に応じた値を加算していきます。

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

アキュムレータは、データ処理の基本です。処理しながら値を蓄積していくことができます。

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) 平均の計算

平均は、カウンタとアキュムレータの両方のパターンを組み合わせたものです。合計(アキュムレータ)と件数(カウンタ)の両方が必要で、最後に割り算を行います。

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

重要: 0 で割らないように必ず確認する:

平均を計算するときは、カウンタが 0 でないことを必ず確認しなければなりません。

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

このような条件の扱いについては、第8章でさらに詳しく学びます。

4.8.4) 最大値・最小値の更新(ランニング最大・最小)

これまでに見た値のうち最大値や最小値を追跡したい場合があります。max()min() 関数を使ってこれを実装できます。

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) 整数のビット演算子: &, |, ^, <<, >>(概要)

ビット演算子は、整数を構成するビット(2進数の 0 と 1)単位で処理を行います。これらの演算子は、算術演算子ほど日常的には使いませんが、フラグや権限の扱い、低レベルなデータ操作、性能最適化など、特定の用途で重要になります。

この節では、その概要だけを紹介します。ビット演算を深く理解するには、2進数表現についての理解が必要であり、この入門的な章で十分に扱える範囲を超えます。

4.9.1) 2進数表現の理解(簡単な導入)

コンピュータは、整数をビット(0 と 1)の並びとして保存しています。たとえば:

  • 10進数の 5 は 2進数で 101(1×4 + 0×2 + 1×1)
  • 10進数の 12 は 2進数で 1100(1×8 + 1×4 + 0×2 + 0×1)

Python の bin() 関数を使うと、整数の 2進数表現を確認できます。

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 (&)

& 演算子はビット AND を行います。結果の各ビットは、両方のオペランドの対応するビットがともに 1 のときだけ 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 は、特定のビットが立っているか(フラグが設定されているか)を調べるのによく使われます。

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 (|)

| 演算子はビット OR を行います。結果の各ビットは、どちらか一方のオペランドの対応するビットが 1 であれば 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 は、複数のフラグを合成するのに使われます。

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 (^)

^ 演算子はビット XOR(排他的論理和)を行います。結果の各ビットは、2つのオペランドの対応するビットが異なる場合に 1 になります。

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 には、ビットを反転したり、値を入れ替えたりできるなど、興味深い性質があります。

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) 左シフト (<<) と右シフト (>>)

<< 演算子はビットを左にシフトし、>> 演算子はビットを右にシフトします。

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)

左へ n ビットシフトすることは 2^n 倍、右へシフトすることは 2^n で割る(切り捨て割り算)ことに相当します。

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) ビット演算子を使う場面

ビット演算子は、次のような場面で役立ちます。

  • フラグや権限の管理(Unix/Linux のファイル権限など)
  • 低レベルなデータ操作(複数の値を1つの整数に詰め込むなど)
  • ネットワークプログラミング(IP アドレスやプロトコルフラグの操作)
  • グラフィックスやゲームプログラミング(色の操作、衝突判定など)
  • 性能最適化(2 のべき乗による掛け算/割り算をシフトで高速化)

多くの日常的なプログラミングでは、ビット演算子を使う機会はあまりありません。しかし、システムプログラミングやネットワーク、性能重視のコードを書くときには、これらの演算子が重要な武器になります。

注意: ここでは基本的な部分だけを扱いました。負の数に対するビット演算(2 の補数表現など)はさらに複雑で、この入門では扱いきれません。今のところは、こうした演算子が存在し、どのような働きをするのかを大まかに理解しておけば十分です。

4.10) 浮動小数点の精度と丸め誤差(やさしい説明)

Python(およびほとんどのプログラミング言語)の浮動小数点数は、非常に広い範囲の値を表現できます。ごく小さな分数から非常に大きな数まで扱えます。しかし、初心者が驚きがちな制約もあります。それは、「すべての 10 進小数を正確に表現できるわけではない」という点です。この節では、その理由と、プログラムにどのような影響があるかを説明します。

4.10.1) 浮動小数点数が常に正確とは限らない理由

コンピュータは浮動小数点数を 10 進(base 10)ではなく 2 進(base 2)で保存します。私たちにとって簡単に思える 10 進小数の一部は、2 進では正確に表現できません。これは、10 進で 1/3 を正確に書けない(0.333333... と無限に続く)こととよく似ています。

次の例を見てみましょう。

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

これは Python のバグではなく、コンピュータが浮動小数点数を表現する方法に起因する根本的な制限です。10 進の 0.1 は 2 進浮動小数点数としては正確に表現できません。ちょうど 1/3 を 10 進で完全には書けないのと同じです。

4.10.2) 表現上の問題をもう少し詳しく

この挙動をさらに確認してみましょう。

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)

0.5, 0.25, 0.125 のように 2 のべき乗(あるいはそれらの和)で表現できる数は正確に表現できますが、ほとんどの 10 進小数はそうではありません。

4.10.3) 実務的な影響

多くの日常的なプログラミングでは、これらの小さな誤差は問題になりません。しかし、いくつかの場面では、この点を意識しておく必要があります。

浮動小数点数どうしの比較:

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)

繰り返し計算での誤差の蓄積:

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

金額計算:

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) 浮動小数点数を扱うための戦略

表示用に丸める:

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

浮動小数点数を厳密な等価で比較しない:

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

精度が重要なときには注意する:

科学計算や金額計算、その他厳密な 10 進小数計算が重要な分野では、次のようなものが必要になる場合があります。

  • 厳密な 10 進小数演算用の decimal モジュール
  • 有理数を正確に扱う fractions モジュール
  • 計算の適切なタイミングでの丸め

これらのモジュールについて、この章では詳細に扱いませんが、存在を知っておくとよいでしょう。

4.10.5) 浮動小数点の精度が問題とならない場合

多くの日常的なプログラミングタスクでは、浮動小数点の精度は十分すぎるほどです。

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

表示時に丸めを行ったり、測定誤差に比べて十分に小さい誤差しか含まれないような用途であれば、浮動小数点演算は問題なく使えます。

4.10.6) 重要なポイント

浮動小数点数は実数の近似表現です。多くのプログラミングタスクでは、これで十分な精度が得られます。ただし、次の点は覚えておきましょう。

  1. 浮動小数点数を厳密な等価比較しない — 代わりに「十分近いか」を判定する
  2. 表示時には丸める — 意味のない桁数を表示しないようにする
  3. 繰り返し計算による誤差の蓄積に注意する
  4. 金額計算では、適切な精度への丸めや decimal モジュールの利用を検討する

これらの制約を理解しておくことで、より堅牢なプログラムを書けるようになり、意外なバグも避けられます。とはいえ、ほとんどのプログラミングタスクでは、ここで挙げた簡単な指針に従えば、浮動小数点演算は「そのまま使って問題ない」ことがほとんどです。

4.11) (オプション)math モジュールによる基本的な数学関数(sqrt, floor, ceil, pi)

Python の組み込み演算子と関数だけで基本的な算術はカバーできますが、より高度な数学的処理には math モジュールが用意されています。モジュール (module) とは、関連する関数や値の集まりであり、プログラム内で利用できます。モジュールについて本格的に学ぶのは第22章ですが、ここでは math モジュールを使うために必要な最低限のことを紹介します。

4.11.1) math モジュールのインポート

math モジュールを使うには、プログラムの最初でインポートする必要があります。

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

モジュールをインポートすると、そのモジュール内のすべての関数や値にアクセスできるようになります。利用するときは、モジュール名、ドット、関数名(または値の名前)の順に書きます。

4.11.2) 数学定数: pie

math モジュールには、重要な数学定数の正確な値が含まれています。

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) sqrt() による平方根

sqrt() 関数は、数値の平方根を計算します。

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

sqrt() は完全平方数に対しても常に浮動小数点数を返します。また、負の数の平方根は(sqrt() では)計算できず、エラーになります。

4.11.4) floor()ceil() による切り下げと切り上げ

floor() 関数は、「元の値以下の最大の整数」(負の無限大方向への切り下げ)を返し、ceil() 関数は「元の値以上の最小の整数」(正の無限大方向への切り上げ)を返します。

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)

これらの関数は、値を特定の範囲に収めたいときや、特定の丸め方で整数結果が必要なときに便利です。

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) floor(), ceil(), round(), int() の比較

これらの丸め方法の違いを理解しておくと役立ちます。

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

これらの関数の振る舞いを図で表すと次のようになります。

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) その他の便利な math モジュールの関数

math モジュールには他にも多くの関数があります。ここでは、よく使われるものをいくつか紹介します。

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

特に isclose() 関数は、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) math モジュールを使うべき場面

math モジュールは、次のような場合に使います。

  • 数学定数(π, e)が必要なとき
  • 平方根やその他の根を計算するとき
  • 三角関数(sin, cos, tan)が必要なとき
  • 対数や指数関数を使うとき
  • 正確な丸め制御(floor, ceil)が必要なとき
  • 浮動小数点数どうしの比較(isclose)を行うとき

基本的な算術(+, -, *, /, //, %, **)には、Python の組み込み演算子を使います。より高度な数学的処理が必要なときには、math モジュールをインポートして使います。

math モジュールは Python の標準ライブラリの一部であり、常に利用可能です。インポートさえすれば使えます。モジュールやインポートシステムについては、第22章で詳しく学びます。


この章では、Python における数値の扱い方として、算術演算の実行方法、異なる種類の割り算の理解、演算子の優先順位の使い方、整数と浮動小数点数の混在処理、組み込み数値関数の利用方法、よくある数値パターン、ビット演算子の利用、浮動小数点の精度とその限界、そして math モジュールによる高度な演算までを学びました。

これらの数値演算は、簡単な計算から複雑なデータ分析に至るまで、無数のプログラミングタスクの土台となります。今後、Python を学び進める中で、ここで扱った操作は、制御構造やコレクション型(今後の章で学びます)と組み合わせて、何度も何度も使うことになるでしょう。

これらの操作が自然に使えるまで、繰り返し練習してみてください。温度の変換、面積や体積の計算、金額データの処理、計測値の分析など、現実世界の量を計算する小さなプログラムを書いてみましょう。練習すればするほど、Python の数値機能を自在に使いこなせるようになります。

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