4

を に変換するfloatDecimal、 にDecimalは可能な限り正確な 2 進数の表現が含まれます。正確であることは素晴らしいことですが、常にあなたが望むものとは限りません。多くの 10 進数は 2 進数で正確に表すことができないため、結果Decimalは少しずれて、少し高くなったり、低くなったりします。

>>> from decimal import Decimal
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
    print Decimal(f)

0.1000000000000000055511151231257827021181583404541015625
0.299999999999999988897769753748434595763683319091796875
10000000000000000905969664
9999999999999999583119736832
1.000000000000099920072216264088638126850128173828125

理想的にはDecimal、最も可能性の高い 10 進数に丸めます。

str文字列からのDecimal作成は正確になるため、に変換してみました。残念ながらstr丸めすぎです。

>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
    print Decimal(str(f))

0.1
0.3
1E+25
1E+28
1.0

Decimalフロートからきれいに丸める方法はありますか?

4

2 に答える 2

4

は、 a を文字列reprに変換するよりも優れた仕事をすることがわかりました。これは、変換を行うための迅速かつ簡単な方法です。floatstr

>>> 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関数により、精度と精度がよりよく一致します。

于 2013-09-19T04:06:06.733 に答える
0

Floatという名前のメソッドで、これを Pharo Smalltalk に実装しましたasMinimalDecimalFraction

これは、正しい丸め (最も近いもの) を前提として、同じ float/double として再解釈される最短の小数を出力するのとまったく同じ問題です。

浮動小数点数の `.` の後の桁数を数える で私の回答を参照してください 。より多くの参照のために

于 2013-09-19T22:09:23.933 に答える