Pythonの浮動小数点数をうまく表します
-
27-09-2019 - |
質問
私は、いくつかの重要な数字に丸められた文字列として、浮動小数点数を表現し、指数形式を使用しないようにしたいと思います。基本的に、浮遊点数を表示し、「見栄えが良い」ことを確認したいと思います。
この問題にはいくつかの部分があります:
- かなりの数字の数を指定できる必要があります。
- 重要な数字の数は変動する必要がありますが、 文字列のフォーマット演算子. 。 [編集]私は修正されました。文字列のフォーマットオペレーターはこれを行うことができます。
- 1.9999999999のようなものではなく、人が期待するように丸められる必要があります
私はこれを行う1つの方法を考え出しましたが、それは仕事ラウンドのように見えますが、それは完全には完全ではありません。 (最大精度は15桁です。)
>>> def f(number, sigfig):
return ("%.15f" % (round(number, int(-1 * floor(log10(number)) + (sigfig - 1))))).rstrip("0").rstrip(".")
>>> print f(0.1, 1)
0.1
>>> print f(0.0000000000368568, 2)
0.000000000037
>>> print f(756867, 3)
757000
これを行うためのより良い方法はありますか?なぜPythonにこれのための機能が組み込まれていないのですか?
解決
(1)最初の重要な数字が15番目の小数点以降に表示され、(2)科学表記ではないフロートを印刷することを可能にする組み込みの文字列フォーマットトリックはないようです。そのため、手動の文字列操作が残ります。
以下に使用します decimal
フロートから小数点桁を抽出するモジュール。 float_to_decimal
機能は、フロートをaに変換するために使用されます Decimal
物体。明らかな方法 decimal.Decimal(str(f))
なぜなら間違っているからです str(f)
かなりの数字を失う可能性があります。
float_to_decimal
から持ち上げられました 小数モジュールのドキュメント.
小数桁がINTのタプルとして取得されたら、以下のコードは明らかなことを行います。必要に応じて、必要に応じて目的の数字を切り取り、桁を文字列に結合し、サインにタックし、小数点を置きます。必要に応じて左または右にポイントとゼロ。
一番下に、私がテストするために使用したいくつかのケースが見つかります f
働き。
import decimal
def float_to_decimal(f):
# http://docs.python.org/library/decimal.html#decimal-faq
"Convert a floating point number to a Decimal with no loss of information"
n, d = f.as_integer_ratio()
numerator, denominator = decimal.Decimal(n), decimal.Decimal(d)
ctx = decimal.Context(prec=60)
result = ctx.divide(numerator, denominator)
while ctx.flags[decimal.Inexact]:
ctx.flags[decimal.Inexact] = False
ctx.prec *= 2
result = ctx.divide(numerator, denominator)
return result
def f(number, sigfig):
# http://stackoverflow.com/questions/2663612/nicely-representing-a-floating-point-number-in-python/2663623#2663623
assert(sigfig>0)
try:
d=decimal.Decimal(number)
except TypeError:
d=float_to_decimal(float(number))
sign,digits,exponent=d.as_tuple()
if len(digits) < sigfig:
digits = list(digits)
digits.extend([0] * (sigfig - len(digits)))
shift=d.adjusted()
result=int(''.join(map(str,digits[:sigfig])))
# Round the result
if len(digits)>sigfig and digits[sigfig]>=5: result+=1
result=list(str(result))
# Rounding can change the length of result
# If so, adjust shift
shift+=len(result)-sigfig
# reset len of result to sigfig
result=result[:sigfig]
if shift >= sigfig-1:
# Tack more zeros on the end
result+=['0']*(shift-sigfig+1)
elif 0<=shift:
# Place the decimal point in between digits
result.insert(shift+1,'.')
else:
# Tack zeros on the front
assert(shift<0)
result=['0.']+['0']*(-shift-1)+result
if sign:
result.insert(0,'-')
return ''.join(result)
if __name__=='__main__':
tests=[
(0.1, 1, '0.1'),
(0.0000000000368568, 2,'0.000000000037'),
(0.00000000000000000000368568, 2,'0.0000000000000000000037'),
(756867, 3, '757000'),
(-756867, 3, '-757000'),
(-756867, 1, '-800000'),
(0.0999999999999,1,'0.1'),
(0.00999999999999,1,'0.01'),
(0.00999999999999,2,'0.010'),
(0.0099,2,'0.0099'),
(1.999999999999,1,'2'),
(1.999999999999,2,'2.0'),
(34500000000000000000000, 17, '34500000000000000000000'),
('34500000000000000000000', 17, '34500000000000000000000'),
(756867, 7, '756867.0'),
]
for number,sigfig,answer in tests:
try:
result=f(number,sigfig)
assert(result==answer)
print(result)
except AssertionError:
print('Error',number,sigfig,result,answer)
他のヒント
浮遊点の精度が必要な場合は、 decimal
モジュール Python Standard Library:
>>> import decimal
>>> d = decimal.Decimal('0.0000000000368568')
>>> print '%.15f' % d
0.000000000036857
これは、指定されたエラーバーに従って値をフォーマットするスニペットです。
from math import floor, log10, round
def sigfig3(v, errplus, errmin):
i = int(floor(-log10(max(errplus,errmin)) + 2))
if i > 0:
fmt = "%%.%df" % (i)
return "{%s}^{%s}_{%s}" % (fmt % v,fmt % errplus, fmt % errmin)
else:
return "{%d}^{%d}_{%d}" % (round(v, i),round(errplus, i), numpy.round(i))
例:
5268685 (+1463262,-2401422) becomes 5300000 (+1500000,-2400000)
0.84312 +- 0.173124 becomes 0.84 +- 0.17
この質問に適切に答えるには、任意の精度フロートが必要です。したがって、 小数モジュール 必須です。指数形式(元の質問の一部)を使用せずに小数を文字列に変換する方法はないので、それを行うための関数を書きました。
def removeExponent(decimal):
digits = [str(n) for n in decimal.as_tuple().digits]
length = len(digits)
exponent = decimal.as_tuple().exponent
if length <= -1 * exponent:
zeros = -1 * exponent - length
digits[0:0] = ["0."] + ["0"] * zeros
elif 0 < -1 * exponent < length:
digits.insert(exponent, ".")
elif 0 <= exponent:
digits.extend(["0"] * exponent)
sign = []
if decimal.as_tuple().sign == 1:
sign = ["-"]
print "".join(sign + digits)
問題は、かなりの人物に丸めようとすることです。 Decimalの「Quantize()」メソッドは小数点よりも高く丸くなく、「round()」関数は常にフロートを返します。これらがバグかどうかはわかりませんが、それは無限の精度の浮動小数点数を丸める唯一の方法は、それをリストまたは文字列として解析し、丸めを手動で行うことであることを意味します。言い換えれば、この質問に対する正気な答えはありません。