は、 a を文字列repr
に変換するよりも優れた仕事をすることがわかりました。これは、変換を行うための迅速かつ簡単な方法です。float
str
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print Decimal(repr(f))
0.1
0.3
1E+25
1E+28
1.0000000000001
それを発見する前に、私は丸めを行う力ずくの方法を思いつきました。大きな数が 15 桁まで正確であることを認識するという利点がありますrepr
。上記の方法では、1e25 および 1e28 の例では有効桁数が 1 桁しか認識されません。
from decimal import Decimal,DecimalTuple
def _increment(digits, exponent):
new_digits = [0] + list(digits)
new_digits[-1] += 1
for i in range(len(new_digits)-1, 0, -1):
if new_digits[i] > 9:
new_digits[i] -= 10
new_digits[i-1] += 1
if new_digits[0]:
return tuple(new_digits[:-1]), exponent + 1
return tuple(new_digits[1:]), exponent
def nearest_decimal(f):
sign, digits, exponent = Decimal(f).as_tuple()
if len(digits) > 15:
round_up = digits[15] >= 5
exponent += len(digits) - 15
digits = digits[:15]
if round_up:
digits, exponent = _increment(digits, exponent)
while digits and digits[-1] == 0 and exponent < 0:
digits = digits[:-1]
exponent += 1
return Decimal(DecimalTuple(sign, digits, exponent))
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print nearest_decimal(f)
0.1
0.3
1.00000000000000E+25
1.00000000000000E+28
1.0000000000001
編集:ブルートフォース丸めを使用するもう1つの理由を発見しました。repr
基になるビット表現を一意に識別する文字列を返そうとしますが、float
必ずしも最後の桁の精度が保証されるわけではありません。使用する桁数を 1 つ減らすことで、丸め関数は期待どおりの数値になることがよくあります。
>>> print Decimal(repr(2.0/3.0))
0.6666666666666666
>>> print dec.nearest_decimal(2.0/3.0)
0.666666666666667
で作成された 10 進数repr
は、実際にはより正確ですが、存在しない精度レベルを意味します。このnearest_decimal
関数により、精度と精度がよりよく一致します。