回答:
最初に、いくつかのコピーアンドペーストコードが必要な人のための関数:
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))
最初のステップである文字列への変換は、同じバイナリ表現を生成し、異なる方法で切り捨てる必要がある浮動小数点リテラル(つまり、ソースコードで記述したもの)のペアがいくつかあるため、非常に困難です。たとえば、0.3と0.29999999999999998を考えます。0.3
Pythonプログラムで記述する場合、コンパイラーはIEEE浮動小数点形式を使用してそれをビットのシーケンスにエンコードします(64ビットの浮動小数点数を想定)。
0011111111010011001100110011001100110011001100110011001100110011
これは、IEEE floatとして正確に表すことができる0.3に最も近い値です。ただし0.29999999999999998
、Pythonプログラムで記述した場合、コンパイラはそれをまったく同じ値に変換します。ある場合には、として(1桁に)切り捨てられることを意味しましたが0.3
、別の場合には0.2
、として切り捨てられることを意味しましたが、Pythonは1つの回答しか提供できません。これはPythonの根本的な制限であり、実際、遅延評価のないプログラミング言語です。切り捨て関数は、コンピュータのメモリに格納されているバイナリ値にのみアクセスでき、実際にソースコードに入力した文字列にはアクセスできません。1
ビットのシーケンスをデコードして10進数に戻すと、再びIEEE 64ビット浮動小数点形式を使用して、
0.2999999999999999888977697537484345957637...
そのため、0.2
おそらくあなたが望んでいないことであっても、素朴な実装が思い付きます。浮動小数点表現エラーの詳細については、Pythonチュートリアルを参照してください。
ラウンド数に非常に近いが、意図的にそのラウンド数と等しくない浮動小数点値を処理することは非常にまれです。したがって、切り捨てを行う場合、メモリ内の値に対応する可能性のあるすべての中から「最も適切な」10進数表現を選択することはおそらく意味があります。Python 2.7以降(3.0は除く)には、それを行うための高度なアルゴリズムが含まれています。これには、デフォルトの文字列フォーマット操作でアクセスできます。
'{}'.format(f)
唯一の注意点は、数値が十分に大きいか小さい場合にg
指数表記(1.23e+4
)を使用するという意味で、これはフォーマット仕様のように機能することです。したがって、メソッドはこのケースをキャッチして、異なる方法で処理する必要があります。f
代わりにフォーマット指定を使用すると問題が発生する場合がいくつかあります。たとえば、3e-10
28桁の精度に切り捨てようとする(それによってが生成される0.0000000002999999999999999980
)などですが、それらをどのように処理するのが最善かはまだわかりません。
丸め数値に非常に近いs を実際に操作しているfloat
が、意図的にそれらと等しくない場合(0.29999999999999998または99.959999999999994など)、これによりいくつかの誤検出が発生します。つまり、丸めたくない数値が丸められます。その場合の解決策は、固定精度を指定することです。
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
ここで使用する精度の桁数は重要ではありません。文字列変換で実行される丸めが値を適切な10進表現に「増加」させないことを保証するのに十分な大きさである必要があります。私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
変数に値を渡す浮動小数点リテラルが見つかるまでプログラムの実行を逆にたどる必要があるため、変数を渡す場合に対処するためにこれを一般化すると、原因が失われたように見えます。1つでもある場合。ほとんどの変数は、ユーザー入力または数式から初期化されます。この場合、バイナリ表現はすべてです。
applymap()
)。操作全体をより効率的にする方法があるかもしれませんが、それは別の問題の問題でしょう。
round(1.923328437452, 3)
標準タイプに関するPythonのドキュメントを参照してください。ラウンド関数に到達するには、少し下にスクロールする必要があります。基本的に、2番目の数値は、小数点以下の桁数を丸めます。
の結果round
は浮動小数点なので、注意してください(例はPython 2.6からのものです)。
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
書式設定された文字列を使用するほうがよいでしょう。
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
round(1.23456, 3)
is 1.235
and not1.2350000000000001
n = 1.923328437452
str(n)[:4]
2
、ユーザーがと入力した場合、.
文字列の最後に小数点が表示されます-本当に良い解決策ではないと思います。
それを行う本当にPythonの方法は
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
以下:
from decimal import Decimal as D, ROUND_DOWN
D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)
更新
通常、問題は浮動小数点数自体の切り捨てではなく、丸め前の浮動小数点数の不適切な使用にあります。
例:int(0.7*3*100)/100 == 2.09
。
浮動小数点数を使用せざるを得ない場合(たとえば、でコードを高速化している場合numba
)、セントを価格の「内部表現」として使用し70*3 == 210
、入力/出力を乗算/除算することをお勧めします。
int(0.7*3*100)/100 == 2.09
。私の1セントはどこに行きましたか?
ImportError: cannot import name 'D'
、あなたは名前付きインポートを作りたくなかったと思いますか?
この質問に対する答えの多くは、完全に間違っています。(切り捨てるのではなく)floatを切り上げるか、すべてのケースで機能するわけではありません。
これは、「Python truncate float」を検索したときのGoogleの検索結果のトップです。この概念は本当に簡単で、より良い答えに値します。decimal
モジュールを使用することはこれを行うpythonicの方法であるというハッチキンスに同意するので、質問に正しく答えると思い、すべての場合に期待どおりに機能する関数をここに与えます。
補足として、一般的に、小数値は2進浮動小数点変数では正確に表すことができません(これについては、こちらを参照してください)。これが、関数が文字列を返す理由です。
from decimal import Decimal, localcontext, ROUND_DOWN
def truncate(number, places):
if not isinstance(places, int):
raise ValueError("Decimal places must be an integer.")
if places < 1:
raise ValueError("Decimal places must be at least 1.")
# If you want to truncate to 0 decimal places, just do int(number).
with localcontext() as context:
context.rounding = ROUND_DOWN
exponent = Decimal(str(10 ** - places))
return Decimal(str(number)).quantize(exponent).to_eng_string()
私はこのようなことをしました:
from math import trunc
def truncate(number, decimals=0):
if decimals < 0:
raise ValueError('truncate received an invalid value of decimals ({})'.format(decimals))
elif decimals == 0:
return trunc(number)
else:
factor = float(10**decimals)
return trunc(number*factor)/factor
できるよ:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
テスト:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
パンダのdfを使用するとき、これは私のために働きました
import math
def truncate(number, digits) -> float:
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper
df['trunc'] = df['float_val'].apply(lambda x: truncate(x,1))
df['trunc']=df['trunc'].map('{:.1f}'.format)
古い "make round()with floor()"トリックの
round(f) = floor(f+0.5)
round()からfloor()を作るために向きを変えることができます
floor(f) = round(f-0.5)
これらのルールはどちらも負の数を回避しますが、その使用は理想的とは言えません。
def trunc(f, n):
if f > 0:
return "%.*f" % (n, (f - 0.5*10**-n))
elif f == 0:
return "%.*f" % (n, f)
elif f < 0:
return "%.*f" % (n, (f + 0.5*10**-n))
使用する一般的でシンプルな関数:
def truncate_float(number, length):
"""Truncate float numbers, up to the number specified
in length that must be an integer"""
number = number * pow(10, length)
number = int(number)
number = float(number)
number /= pow(10, length)
return number
def precision(value, precision):
"""
param: value: takes a float
param: precision: int, number of decimal places
returns a float
"""
x = 10.0**precision
num = int(value * x)/ x
return num
precision(1.923328437452, 3)
1.923
私の意見ではほとんどの回答が複雑すぎるようですが、これはどうですか?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
「。」のインデックスをスキャンするだけです。必要に応じて切り捨てます(丸めなし)。最後のステップとして文字列を浮動小数点に変換します。
または、あなたの場合、入力として浮動小数点数を取得し、出力として文字列が必要な場合:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
ライブラリやその他の外部依存関係がなく、リスト内包に収まるほど単純なもの。Python> = 3.6の場合、f文字列を使用して記述するのは非常に簡単です。
アイデアは、文字列変換に必要以上の1桁に丸めを実行させ、最後の桁を切り捨てることです。
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1] for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.800', '0.888', '1.125', '1.250', '1.500']
もちろん、ここでは四捨五入が行われます(つまり、4桁目です)が、ある時点での四捨五入は避けられません。切り捨てと丸めの間の移行に関連がある場合は、少し良い例を次に示します。
>>> nacc = 6 # desired accuracy (maximum 15!)
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nacc}f}'[:-(nacc-nout)] for x in [2.9999, 2.99999, 2.999999, 2.9999999]]
>>> ['2.999', '2.999', '2.999', '3.000']
おまけ:右側のゼロを削除する
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1].rstrip('0') for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.8', '0.888', '1.125', '1.25', '1.5']
python初心者でもあり、ここでいくつかの断片を使用した後、2セントを提供します
print str(int(time.time()))+str(datetime.now().microsecond)[:3]
str(int(time.time()))は、時間エポックをintとして受け取り、それを文字列に変換して結合します... str(datetime.now()。microsecond)[:3]これはマイクロ秒のみを返し、変換します文字列に変換し、最初の3文字に切り捨てます
# value value to be truncated
# n number of values after decimal
value = 0.999782
n = 3
float(int(value*1en))*1e-n