Pythonに限った話ではありませんが、コンピューターで小数点の計算をさせると誤差が生じるため、期待通りの計算結果が得られないことがあります。
例えば、0.1+0.2 は 0.3 ではなく、0.30000000000000004 となります。
10進数小数を正確に計算するためにdecimalモジュールがよく使われますが、割り算で罠があり、1÷60×60 は 60 ではなく、59.99999999999999999999999999 となります。
(1÷60が割り切れず、丸め誤差が生じる)
そこで、10進数小数を分数として扱える便利なものとして、fractionsモジュールがあります。fractionsモジュールを使うと上記の「0.1+0.2」や「1÷60×60」が正しく計算できます。
しかし、fractionモジュールで計算は処理が重いです。
float、decimal、fractionそれぞれで同じ計算をした結果を比較してみました。
計算内容:
小数点以下の数字をランダムに、1000個を生成して、それらを1000回づつ加減乗除した計算を行い、最後に時間を計測して表示します。
コード:
通常のfloatでの処理
import random
import time
random_numbers = [random.random() for _ in range(1000)]
operations = ['+', '-', '*', '/']
total_operations = 1000
start_time = time.time()
for _ in range(total_operations):
result = random_numbers[0]
for i in range(1, len(random_numbers)):
operation = random.choice(operations)
num = random_numbers[i]
if operation == '+':
result += num
elif operation == '-':
result -= num
elif operation == '*':
result *= num
elif operation == '/':
result /= num
end_time = time.time()
elapsed_time = end_time - start_time
print("Elapsed Time:", elapsed_time, "seconds")
decimalモジュールを利用した処理
import random
from decimal import Decimal, getcontext
import time
getcontext().prec = 28 # 精度を設定(必要に応じて調整してください)
random_numbers = [Decimal(random.random()) for _ in range(1000)]
operations = ['+', '-', '*', '/']
total_operations = 1000
start_time = time.time()
for _ in range(total_operations):
result = random_numbers[0]
for i in range(1, len(random_numbers)):
operation = random.choice(operations)
num = random_numbers[i]
if operation == '+':
result += num
elif operation == '-':
result -= num
elif operation == '*':
result *= num
elif operation == '/':
result /= num
end_time = time.time()
elapsed_time = end_time - start_time
print("Elapsed Time:", elapsed_time, "seconds")
fractionモジュールを利用した処理
import random
from fractions import Fraction
import time
random_numbers = [random.random() for _ in range(1000)]
operations = ['+', '-', '*', '/']
total_operations = 1000
start_time = time.time()
for _ in range(total_operations):
result = Fraction(random_numbers[0])
for i in range(1, len(random_numbers)):
operation = random.choice(operations)
num = Fraction(random_numbers[i])
if operation == '+':
result += num
elif operation == '-':
result -= num
elif operation == '*':
result *= num
elif operation == '/':
result /= num
end_time = time.time()
elapsed_time = end_time - start_time
print("Elapsed Time:", elapsed_time, "seconds")
結果:
以下のようになりました。floatとdecimalは、さほど差がないですが、fractionはかなり遅いことがわかります。
fractions : Elapsed Time: 80.84469294548035 seconds
float : Elapsed Time: 0.4394075870513916 seconds
decimal : Elapsed Time: 0.43607521057128906 seconds