[解決済み] float値を切り捨てるには?
質問
浮動小数点以下の桁数が一定になるように桁上げを行いたい。
1.923328437452 → 1.923
printではなく、別の関数に文字列として出力したいのですが。
また、失われた数字を丸めるのではなく、無視したいです。
どのように解決するのですか?
まず、関数は、いくつかのコピーアンドペーストコードをしたい人のために。
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '{}'.format(f)
if 'e' in s or 'E' in s:
return '{0:.{1}f}'.format(f, n)
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
これはPython 2.7と3.1+で有効です。それ以前のバージョンでは、同じ "インテリジェント丸め" の効果を得ることはできません(少なくとも、多くの複雑なコードなしではできません)が、切り捨ての前に小数点以下12桁に丸めることは多くの場合うまくいきます。
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '%.12f' % f
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
説明
基本的な方法の核心は、値を完全な精度で文字列に変換し、その後、必要な文字数以上のすべてを切り落とすことです。後者のステップは簡単で、文字列操作で行うことができます。
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
または
decimal
モジュール
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
最初のステップである文字列への変換は非常に困難です。なぜなら、浮動小数点数リテラル(つまり、ソースコードに書かれているもの)には、どちらも同じ2進数表現を生成し、かつ異なる切り捨てをすべきペアが存在するからです。例えば、0.3と0.2999999999998を考えてみましょう。もしあなたが
0.3
と書くと、コンパイラはこれをIEEE浮動小数点フォーマットを使って次のようなビット列にエンコードします(64ビット浮動小数点と仮定)。
0011111111010011001100110011001100110011001100110011001100110011
これは、IEEE floatとして正確に表現できる0.3に最も近い値です。しかし、もしあなたが
0.29999999999999998
と書くと、コンパイラはそれを
という全く同じ値
. あるケースでは、次のように(1桁に)切り捨てることを意味しました。
0.3
のように切り捨てるのに対して、もう一方のケースでは、次のように切り捨てることを意味します。
0.2
のように切り詰められることを意味しますが、Pythonは1つの答えしか与えることができません。これはPythonの、いや、遅延評価のないあらゆるプログラミング言語の基本的な制限です。切り捨て関数はコンピュータのメモリに保存されたバイナリ値にしかアクセスできず、実際にソースコードに入力された文字列にはアクセスできません。
1
IEEE の 64 ビット浮動小数点フォーマットを使用して、ビットのシーケンスを 10 進数にデコードすると、次のようになります。
0.2999999999999999888977697537484345957637...
ということで、素朴な実装では、以下のようになります。
0.2
と表示されます。浮動小数点数表示のエラーについて詳しくは
Pythonチュートリアルを参照してください。
.
丸い数に近い浮動小数点値を扱うことは非常に稀であり、なおかつ 意図的に である浮動小数点値を扱うことは非常に稀です。ですから、切り捨てるときには、メモリ上の値に対応するすべてのものの中から、quot;nice" の10進表現を選ぶことがおそらく理にかなっています。Python 2.7 以降 (3.0 ではありません) には という洗練されたアルゴリズムがあります。 があり、デフォルトの文字列フォーマット操作でアクセスすることができます。
'{}'.format(f)
唯一の注意点は、これはまるで
g
のフォーマット指定と同じように動作します。
1.23e+4
)を使うという意味で、この書式指定は、数値が十分に大きいか小さいかの違いだけです。そこで、メソッドはこのケースをキャッチして、別の方法で処理しなければならない。を使うケースがいくつかあります。
f
を切り詰めようとするような場合です。
3e-10
を28桁の精度で切り詰めようとすると(これは
0.0000000002999999999999999980
を生成します)、それらをどのように処理するのがベストなのかまだわかりません。
もしあなたが実際に
は
で作業しているのであれば
float
を使用している場合、丸めた数に非常に近いが意図的に等しくないもの (0.29999999998 や 99.95999999994 など) は、いくつかの誤検出を生じます。そのような場合の解決策は、固定精度を指定することです。
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
ここで使用する精度の桁数は実際には重要ではなく、文字列変換で実行される丸めによって値が素敵な10進表現に "bump up" されないようにするために十分大きくする必要があるにすぎません。私は
sys.float_info.dig + n + 2
で十分な場合もありますが、そうでない場合は
2
を増やさなければならないかもしれませんし、増やしておいて損はないでしょう。
Python の以前のバージョン (2.6 または 3.0 まで) では、浮動小数点数のフォーマットはもっと粗雑で、定期的に次のようなものを生成していました。
>>> 1.1
1.1000000000000001
もしこれがあなたの状況なら、もし
する
が切り捨てのために 10 進数表現を使いたい場合、(私の知る限り) できるのは
float
で表現できる完全な精度よりも低い桁数を選んで、切り捨てる前にその桁数に丸めることです。典型的な選択肢は 12 です。
'%.12f' % f
となっていますが、これは使用する数値に合わせて調整してください。
1 まあ... 私は嘘をつきました。厳密には、あなたは ができます。 は、Pythonに自分自身のソースコードを再解析して、切り捨て関数に渡す最初の引数に対応する部分を抽出するように指示します。もしその引数が浮動小数点数のリテラルであれば、小数点以下のある桁数で切り捨ててそれを返せばいいだけです。しかし、この方法は引数が変数の場合はうまくいきませんので、かなり無駄があります。以下は娯楽としてのみ紹介します。
def trunc_introspect(f, n):
'''Truncates/pads the float f to n decimal places by looking at the caller's source code'''
current_frame = None
caller_frame = None
s = inspect.stack()
try:
current_frame = s[0]
caller_frame = s[1]
gen = tokenize.tokenize(io.BytesIO(caller_frame[4][caller_frame[5]].encode('utf-8')).readline)
for token_type, token_string, _, _, _ in gen:
if token_type == tokenize.NAME and token_string == current_frame[3]:
next(gen) # left parenthesis
token_type, token_string, _, _, _ = next(gen) # float literal
if token_type == tokenize.NUMBER:
try:
cut_point = token_string.index('.') + n + 1
except ValueError: # no decimal in string
return token_string + '.' + '0' * n
else:
if len(token_string) < cut_point:
token_string += '0' * (cut_point - len(token_string))
return token_string[:cut_point]
else:
raise ValueError('Unable to find floating-point literal (this probably means you called {} with a variable)'.format(current_frame[3]))
break
finally:
del s, current_frame, caller_frame
変数に値を与える浮動小数点数リテラルを見つけるまで、プログラムの実行をさかのぼらなければならないからです。それがあればの話ですが。ほとんどの変数は、ユーザー入力または数式から初期化され、その場合、バイナリ表現がすべてとなります。
関連
-
Pythonを使って簡単なzipファイルの解凍パスワードを手作業で解く
-
[解決済み】ImportError: sklearn.cross_validation という名前のモジュールがない。
-
[解決済み] プログラムの実行やシステムコマンドの呼び出しはどのように行うのですか?
-
[解決済み] PandasでDataFrameの行を反復処理する方法
-
[解決済み] 環境変数の値にアクセスする方法
-
[解決済み] 文字列をfloatやintにパースするにはどうしたらいいですか?
-
[解決済み] .NETでのdecimal, float, doubleの違い?
-
[解決済み] 文字列が数値(float)であるかどうかを確認するにはどうすればよいですか?
-
[解決済み] JavaScriptで浮動小数点数の精度を扱うには?
-
[解決済み】2つの辞書を1つの式でマージする(辞書の和をとる)には?)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Python 人工知能 人間学習 描画 機械学習モデル作成
-
Python カメの描画コマンドとその例
-
PythonはWordの読み書きの変更操作を実装している
-
Python Pillow Image.save jpg画像圧縮問題
-
[解決済み】終了コード -1073741515 (0xC0000135)でプロセス終了)
-
[解決済み】「SyntaxError.Syntax」は何ですか?Missing parentheses in call to 'print'」はPythonでどういう意味ですか?
-
[解決済み】Python elifの構文が無効です【終了しました
-
[解決済み】IndexError: invalid index to scalar variableを修正する方法
-
[解決済み】Python: SyntaxError: キーワードは式になり得ない
-
[解決済み】なぜ10進数は2進数で正確に表現できないのですか?