2008年9月8日月曜日

Python で正確な小数の計算 (2) - Decimal の値を文字列にして、eval () で評価するときは repr 関数を使う

Python で正確な小数の計算 (1) - Decimal モジュールを使う」のつづき

1. Decimal 型の値を文字列に変換

あるプログラムの中で、

「Decimal 型と、計算式を表す文字列を結合し、eval() 関数で動的に計算を行わせる」

ことをしようとした。その結果、意図していたものと、異なる結果になった。 (@_@;)

例えば、次の計算。

from decimal import Decimal
print eval(str(Decimal("270")) + "/ 180")

1.5 と表示されるかと思いきや 1 と表示された。

なぜだろうと思い、確認してみると、

print Decimal("270") / 180
print 270 / 180

結果は、

1.5
1

つまり、

str(Decimal("270"))

の結果が 270 となっており、Decimal("270") ではないということ。

仕方がないので、

d = Decimal("270")
dstr = "Decimal('" + str(d) + "')"
print eval(dstr + " / 180")

としていたが、どこか釈然としない。

 

2. str() と repr() の違い

str() の親戚に repr() という関数がある。「似たような説明が書かれていたなぁ~」というくらいの認識だった。

2.1 組み込み関数 によると、

str([object])

オブジェクトをうまく印字可能な形に表現したものを含む文字列を返します。文字列に対してはその文字列自体を返します。repr(object) との違いは、str(object) は常に eval() が受理できるような文字列を返そうと試みるわけではないという点です; この関数の目的は印字可能な文字列を返すところにあります。引数が与えられなかった場合、空の文字列 '' を返します。

repr(object)

オブジェクトの印字可能な表現を含む文字列を返します。これは型変換で得られる (逆クオートの) 値と同じです。通常の関数としてこの操作にアクセスできるとたまに便利です。この関数は多くの型について、 eval() に渡されたときに同じ値を持つようなオブジェクトを表す文字列を生成しようとします。

(太字は引用者による)

動作を確認してみた。

print repr(Decimal("270"))
print eval(repr(Decimal("270")) + "/ 180")

結果、

Decimal("270")
1.5

repr 関数を使うと、ちゃんと Decimal 型で表示してくれた。 eval() による計算も正確だ。 eval() に渡すと、元の型のオブジェクトに戻る。

そういえば、前回、小数を代入した変数を print すると内部的な表現では示されず、リストに入れた小数の変数は、リストを print すると内部的な表現で出力された。これは、上記の str() と repr() の差なのかな?

print 0.1
print [0.1]
print str(0.1)
print repr(0.1)

結果は、

0.1
[0.10000000000000001]
0.1
0.10000000000000001

なんとなく、そんな感じ。

 

3. Decimal 型の __repr__(), __str__() の実装

Decimal 型の __repr__() の実装を見たら、

    def __repr__(self):
        """Represents the number as an instance of Decimal."""
        # Invariant:  eval(repr(d)) == d
        return 'Decimal("%s")' % str(self)

それに対して、__str__() は、

    def __str__(self, eng=False, context=None):
        """Return string representation of the number in scientific notation.

        Captures all of the information in the underlying representation.
        """

まさに、コメントが今回の問題を物語っていた。

 

4. 関連記事