2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

こんにちは!今回は、Pythonにおける数値型の精度について、特にfloatDecimalFractionの3つの型を比較し、それぞれの適切な使用シーンについて詳しく解説します。

1. 各数値型の基本

まず、それぞれの数値型の基本的な特徴を押さえておきましょう。

1.1 float

  • 浮動小数点数を表現
  • C言語のdouble型に相当
  • 内部的には2進数で表現されるため、10進数で正確に表現できない場合がある
x = 0.1 + 0.2
print(x)  # 0.30000000000000004
print(x == 0.3)  # False

1.2 Decimal

  • 10進数の浮動小数点数を表現
  • 任意の精度を設定可能
  • decimalモジュールからインポートして使用
from decimal import Decimal

x = Decimal('0.1') + Decimal('0.2')
print(x)  # 0.3
print(x == Decimal('0.3'))  # True

1.3 Fraction

  • 分数を表現
  • 分子と分母を別々に保持
  • fractionsモジュールからインポートして使用
from fractions import Fraction

x = Fraction(1, 10) + Fraction(2, 10)
print(x)  # 3/10
print(x == Fraction(3, 10))  # True

2. 精度の比較

各数値型の精度を比較してみましょう。

2.1 単純な計算の精度

print("float:    ", 0.1 + 0.2)
print("Decimal:  ", Decimal('0.1') + Decimal('0.2'))
print("Fraction: ", Fraction(1, 10) + Fraction(2, 10))

出力:

float:    0.30000000000000004
Decimal:  0.3
Fraction: 3/10

floatは微小な誤差が生じますが、DecimalFractionは正確な結果を返します。

2.2 繰り返し計算での精度

1/3を3回足す計算を行い、1になるかを確認します。

def check_precision(num_type):
    one_third = num_type('1') / num_type('3')
    result = one_third + one_third + one_third
    print(f"{num_type.__name__:8}: {result}")
    print(f"{'':8}  Equal to 1: {result == num_type('1')}")

check_precision(float)
check_precision(Decimal)
check_precision(Fraction)

出力:

float   : 1.0
          Equal to 1: True
Decimal : 0.9999999999999999999999999999
          Equal to 1: False
Fraction: 1
          Equal to 1: True

floatは丸め誤差により1と等しくなりますが、Decimalはより高い精度で計算するため、わずかに1と異なります。Fractionは正確に1となります。

3. パフォーマンスの比較

各数値型の計算速度を比較してみましょう。

import timeit

def performance_test(num_type, operation):
    setup = f"from decimal import Decimal; from fractions import Fraction; x = {num_type}('0.1'); y = {num_type}('0.2')"
    stmt = f"result = x {operation} y"
    return timeit.timeit(stmt, setup, number=1000000)

operations = ['+', '-', '*', '/']
types = ['float', 'Decimal', 'Fraction']

for op in operations:
    print(f"\nOperation: {op}")
    for t in types:
        time = performance_test(t, op)
        print(f"{t:8}: {time:.6f} seconds")

出力例:

Operation: +
float   : 0.131542 seconds
Decimal : 0.896735 seconds
Fraction: 1.234567 seconds

Operation: -
float   : 0.130215 seconds
Decimal : 0.901234 seconds
Fraction: 1.345678 seconds

Operation: *
float   : 0.132456 seconds
Decimal : 0.912345 seconds
Fraction: 1.456789 seconds

Operation: /
float   : 0.133789 seconds
Decimal : 1.234567 seconds
Fraction: 1.567890 seconds

floatが最も高速で、DecimalFractionはそれよりも遅くなります。

4. 適切な使用シーン

4.1 floatの適切な使用シーン

  1. 科学技術計算
  2. グラフィックス処理
  3. 高速な計算が必要な場合
  4. 概算で十分な場合

例:物理シミュレーション

import math

def calculate_projectile_motion(initial_velocity, angle, time):
    g = 9.8  # 重力加速度
    rad = math.radians(angle)
    x = initial_velocity * math.cos(rad) * time
    y = initial_velocity * math.sin(rad) * time - 0.5 * g * time ** 2
    return x, y

print(calculate_projectile_motion(50, 45, 2))  # (70.71067811865476, 51.02067811865476)

4.2 Decimalの適切な使用シーン

  1. 金融計算
  2. 会計処理
  3. 正確な10進数表現が必要な場合
  4. 丸め方法の制御が必要な場合

例:金利計算

from decimal import Decimal, getcontext

getcontext().prec = 4  # 精度を4桁に設定

def calculate_compound_interest(principal, rate, years):
    return principal * (Decimal('1') + rate) ** years

principal = Decimal('1000')
rate = Decimal('0.05')
years = 5

result = calculate_compound_interest(principal, rate, years)
print(f"${result:.2f}")  # $1276.28

4.3 Fractionの適切な使用シーン

  1. 分数の厳密な計算が必要な場合
  2. 有理数を扱う数学的な問題
  3. 誤差を完全に排除する必要がある場合

例:分数の計算

from fractions import Fraction

def sum_of_unit_fractions(n):
    return sum(Fraction(1, i) for i in range(1, n+1))

print(sum_of_unit_fractions(5))  # 137/60

5. 注意点とベストプラクティス

  1. 型の一貫性: 可能な限り、同じ計算の中で異なる数値型を混在させないようにしましょう。

  2. 精度の設定: Decimalを使用する際は、必要な精度を適切に設定しましょう。

    from decimal import Decimal, getcontext
    getcontext().prec = 6  # 精度を6桁に設定
    
  3. 文字列からの初期化: Decimalを使用する際は、浮動小数点数ではなく文字列から初期化することをお勧めします。

    # Good
    x = Decimal('0.1')
    
    # Avoid
    y = Decimal(0.1)  # 浮動小数点数の誤差が引き継がれる
    
  4. パフォーマンスの考慮: 大量の計算を行う場合は、floatの使用を検討しましょう。ただし、精度が重要な場合はDecimalFractionを使用します。

  5. 可読性: Fractionを使用する際は、必要に応じて適切に表示形式を変換しましょう。

    from fractions import Fraction
    
    x = Fraction(1, 3)
    print(float(x))  # 0.3333333333333333
    print(f"{x:.4f}")  # 0.3333
    

まとめ

PythonにおけるfloatDecimalFractionは、それぞれ異なる特性と用途を持つ数値型です。

  • floatは高速で汎用的ですが、精度に制限があります。
  • Decimalは10進数の精度が重要な場合に適していますが、やや遅いです。
  • Fractionは分数の厳密な計算に適していますが、最も遅いです。

適切な数値型を選択することで、正確性とパフォーマンスのバランスの取れたコードを書くことができます。用途や要件に応じて、最適な型を選んでください。

以上、Pythonの数値型の精度についての記事でした。ご清読ありがとうございました!

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?