何がやりたいかと端的に言うと、
- 30.100 => 30.1
- 30.000 => 30
のようにしたい。
Decimalモジュールを使う
小数点といえばDecimalなので、Decimalモジュールを探したらそれっぽいDecimal.normalizeがありました。
数値を正規化 (normalize) して、右端に連続しているゼロを除去し、 Decimal('0') と同じ結果はすべて Decimal('0e0') に変換します。等価クラスの属性から基準表現を生成する際に用います。たとえば、 Decimal('32.100') と Decimal('0.321000e+2') の正規化は、いずれも同じ値 Decimal('32.1') になります。
これは良さそう。早速使ってみます。
from decimal import Decimal s = "30.0" a = Decimal.normalize(Decimal(s)) print(a) # 3E+1
うーん。指数表現になってしまった。
他に使えそうなものを探すと、Context.normalizeがありました。こっちを使ってみましょう。
from decimal import Decimal, Context s = "30.0" context = Context() a = context.normalize(Decimal(s)) print(a) #3E+1
変わらないですね。指数表現じゃなくしてほしいんですが。
色々漁ってると、Decimal FAQにドンピシャなQAがありました。
Q. ある種の十進数値はいつも指数表記で表示されます。指数表記以外の表示にする方法はありますか?
A. 値によっては、指数表記だけが有効桁数を表せる表記法なのです。たとえば、 5.0E+3 を 5000 と表してしまうと、値は変わりませんが元々の2桁という有効数字が反映されません。
もしアプリケーションが有効数字の追跡を等閑視するならば、指数部や末尾のゼロを取り除き、有効数字を忘れ、しかし値を変えずにおくことは容易です:
>>> def remove_exponent(d): ... return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize() >>> remove_exponent(Decimal('5E+3')) Decimal('5000')
なるほど。
結論
というわけで、数値(float or int)から正規化した文字列(str)を返す関数は以下のようになります。
from decimal import Decimal, Context def decimal_normalize(f): """数値fの小数点以下を正規化し、文字列で返す""" def _remove_exponent(d): return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize() a = Decimal.normalize(Decimal(str(f))) b = _remove_exponent(a) return str(b) print(decimal_normalize(30.0)) # 30 print(decimal_normalize(30)) # 30 print(decimal_normalize(30.0001000)) # 30.0001 print(decimal_normalize(0.0)) # 0 print(decimal_normalize(0)) # 0 print(decimal_normalize(-5.0)) # -5
Decimal(x)するときに、xはstr型にする必要はなくてfloat型のままでもいけますが、私はstr型に変換するようにしています。なんか挙動の差があって、str型に変換したほうが安定していたイメージがあるので(うろおぼえ)。
Decimalモジュールを使わない
Decimalモジュールを探す前に書いていたコード。Decimalを使いたくないときはこっちでもまあ。
def decimal_normalize(f): text = str(f) while True: if ("." in text and text[-1] == "0") or (text[-1] == "."): text = text[:-1] continue break return text print(decimal_normalize(30.0)) # 30 print(decimal_normalize(30)) # 30 print(decimal_normalize(30.0001000)) # 30.0001 print(decimal_normalize(0.0)) # 0 print(decimal_normalize(0)) # 0 print(decimal_normalize(-5.0)) # -5