1. ホーム
  2. python

floatを位置情報付き文字列に変換(科学的記数法、虚数精度なし)

2023-12-08 18:48:27

質問

浮動小数点数を常に10進数で表示するようにしたい (例: 12345000000000000000000.0 または 0.000000000000012345 にはない。 科学的表記法 しかし、私は結果が最大で15.7になるようにしたいと思います。 有効数字 であり、それ以上ではありません。

私が欲しいのは 理想的には になるように、その結果が 最短 に変換しても同じ値になる10進数形式の文字列です。 float .

よく知られているように reprfloat は、指数が15より大きい場合は科学的記数法で、-4より小さい場合は科学的記数法で表記されます。

>>> n = 0.000000054321654321
>>> n
5.4321654321e-08  # scientific notation

もし str が使われると、結果の文字列は再び科学的記数法になります。

>>> str(n)
'5.4321654321e-08'


提案されているのは formatf フラグと、科学的記法を取り除くのに十分な精度が必要です。

>>> format(0.00000005, '.20f')
'0.00000005000000000000'

この場合、末尾にゼロが追加されますが、動作します。しかし、同じフォーマットが .1 では同じ書式は失敗し、floatの実際の機械精度を超える小数点以下の桁数を与えてしまいます。

>>> format(0.1, '.20f')
'0.10000000000000000555'

そして、もし私の番号が 4.5678e-20 であれば .20f を使っても相対的な精度は落ちます。

>>> format(4.5678e-20, '.20f')
'0.00000000000000000005'

このように これらのアプローチは私の要求と一致しません。 .


と同じ桁数の任意の浮動小数点数を10進数で表示する最も簡単でパフォーマンスの良い方法は何でしょうか? repr(n) (または str(n) Python 3 の場合) ただし、科学的記数法ではなく、常に10進法を使っています。

つまり、例えばfloatの値を変換する関数や演算は 0.00000005 を文字列 '0.00000005' ; 0.1 から '0.1' ; 420000000000000000.0 から '420000000000000000.0' または 420000000000000000 で、float 値をフォーマットします。 -4.5678e-5 として '-0.000045678' .


懸賞金期間終了後。Karinが文字列操作を使って、Python 2上の私の初期アルゴリズムと比較して大幅な速度向上を達成できることを実証したように、少なくとも2つの実行可能なアプローチがあるようです。

このように

私は主にPython 3で開発しているので、私自身の答えを受け入れ、Karinに報奨金を授与することにします。

どのように解決するのですか?

残念なことに float.__format__ での新しい書式でさえもサポートしていないようです。デフォルトの書式設定である floatrepr と同じです。 f フラグを使用すると、デフォルトで6桁の端数が発生します。

>>> format(0.0000000005, 'f')
'0.000000'


しかし、望む結果を得るためのハックがあります - 最速のものではありませんが、比較的簡単です。

  • を使用して float を文字列に変換します。 str() あるいは repr()
  • とすると、新しい Decimal のインスタンスがその文字列から作成されます。
  • Decimal.__format__ サポート f フラグをサポートしており、これは望ましい結果をもたらします。 float とは異なり、デフォルトの精度ではなく、実際の精度を表示します。

このようにして、簡単なユーティリティ関数 float_to_str :

import decimal

# create a new context for this task
ctx = decimal.Context()

# 20 digits should be enough for everyone :D
ctx.prec = 20

def float_to_str(f):
    """
    Convert the given float to a string,
    without resorting to scientific notation
    """
    d1 = ctx.create_decimal(repr(f))
    return format(d1, 'f')

グローバルな10進数コンテキストを使用しないように注意する必要があるため、この関数のために新しいコンテキストが構築されます。これは最も速い方法です。他の方法としては decimal.local_context を使用することもできますが、新しいスレッドローカルコンテキストと変換ごとのコンテキストマネージャを作成するため、より遅くなります。

この関数は、仮数から可能なすべての桁を丸めた文字列を返すようになりました。 最短の等価表現 :

>>> float_to_str(0.1)
'0.1'
>>> float_to_str(0.00000005)
'0.00000005'
>>> float_to_str(420000000000000000.0)
'420000000000000000'
>>> float_to_str(0.000000000123123123123123123123)
'0.00000000012312312312312313'

最後の結果は下一桁で丸められます。

かりんさん(@Karin)が指摘されているように float_to_str(420000000000000000.0) は厳密には期待された形式と一致しません。 420000000000000000 を返し、末尾に .0 .