Pythonで小数を最も低い形式の小数に変換する方法を知りたいと思っていました。
例えば:
0.25 -> 1/4
0.5 -> 1/2
1.25 -> 5/4
3 -> 3/1
次の2つのオプションがあります。
float.as_integer_ratio()
を使用します。
_>>> (0.25).as_integer_ratio()
(1, 4)
_
(Python 3.6の時点で、 decimal.Decimal()
オブジェクトで同じことを行うことができます 。)
fractions.Fraction()
type を使用します。
_>>> from fractions import Fraction
>>> Fraction(0.25)
Fraction(1, 4)
_
後者には非常に役立つstr()
変換があります:
_>>> str(Fraction(0.25))
'1/4'
>>> print Fraction(0.25)
1/4
_
浮動小数点値は不正確になる可能性があるため、「奇妙な」端数になる可能性があります。 Fraction.limit_denominator()
を使用して、分母を分数をいくらか「単純化」するように制限します。
_>>> Fraction(0.185)
Fraction(3332663724254167, 18014398509481984)
>>> Fraction(0.185).limit_denominator()
Fraction(37, 200)
_
Python 2.6をまだ使用している場合、Fraction()
はまだfloat
を直接渡すことをサポートしていませんが、can上記の2つの手法を組み合わせて:
_Fraction(*0.25.as_integer_ratio())
_
または、単に Fraction.from_float()
クラスメソッド を使用できます。
_Fraction.from_float(0.25)
_
基本的に同じことを行います。整数比のTupleを取得し、2つの別個の引数として渡します。
サンプル値を使用した小さなデモ:
_>>> for f in (0.25, 0.5, 1.25, 3.0):
... print f.as_integer_ratio()
... print repr(Fraction(f)), Fraction(f)
...
(1, 4)
Fraction(1, 4) 1/4
(1, 2)
Fraction(1, 2) 1/2
(5, 4)
Fraction(5, 4) 5/4
(3, 1)
Fraction(3, 1) 3
_
fractions
モジュールとfloat.as_integer_ratio()
メソッドの両方がPython 2.6で新しく追加されました。
from fractions import Fraction
print(Fraction(0.25))
print(Fraction(0.5))
print(Fraction(1.25))
print(Fraction(3))
#1/4
#1/2
#5/4
#3
proper fractionを出力したい場合、この小さなレシピは次のようにします:
from fractions import Fraction
def dec_to_proper_frac(dec):
sign = "-" if dec < 0 else ""
frac = Fraction(abs(dec))
return (f"{sign}{frac.numerator // frac.denominator} "
f"{frac.numerator % frac.denominator}/{frac.denominator}")
これは次のように印刷されます。
>>> dec_to_proper_frac(3.75)
>>> "3 3/4"
より複雑なフロートに固有の不正確さによる追加オプションを使用して、Martijn Pietersの優れた答えを拡張します。例えば:
>>> f = 0.8857097
>>> f.as_integer_ratio()
(1994440937439217, 2251799813685248) # mathematically wrong
>>> Fraction(f)
Fraction(1994440937439217, 2251799813685248) # same result but in a class
>>> Fraction(f).limit_denominator()
Fraction(871913, 984423) # still imprecise
必要な数学的結果は8857097/10000000
これは、文字列にキャストしてから操作することで実現できます。
編集された応答
精度の問題を解決するはるかに簡単な方法を見つけました。
>>> Fraction(str(f))
Fraction(8857097, 10000000)
文字列としてキャストすると、正確なDecimalインスタンスも可能になります
>>> Decimal(f).as_integer_ratio()
(1994440937439217, 2251799813685248)
>>> Decimal(str(f)).as_integer_ratio()
(8857097, 10000000)
元の応答
def float_to_ratio(flt):
if int(flt) == flt: # to prevent 3.0 -> 30/10
return int(flt), 1
flt_str = str(flt)
flt_split = flt_str.split('.')
numerator = int(''.join(flt_split))
denominator = 10 ** len(flt_split[1])
return numerator, denominator
それではテストしましょう:
>>> float_to_ratio(f)
(8857097, 10000000) # mathematically correct
この種の小数部の精度は最適化されておらず、通常は必要ありませんが、完全を期すためにここにあります。この関数は分数を単純化しませんが、追加の処理を行ってそれを減らすことができます。
>>> n = 0.5
>>> float_to_ratio(n)
(5, 10)
>>> Fraction(*float_to_ratio(n))
Fraction(1, 2)