round()が適切に丸められていないようです


123

round()関数のドキュメントには、数値と、小数点以下の位置を丸めて渡すと記載されています。したがって、これ行う必要があります:

n = 5.59
round(n, 1) # 5.6

しかし、実際には、古き良き浮動小数点の奇妙さが忍び寄り、次のような結果になります。

5.5999999999999996

UIのために、を表示する必要があります5.6。私はインターネットをざっと見て、これがPythonの実装に依存しているというドキュメントを見つけました。残念ながら、これは私のWindows開発マシンと私が試した各Linuxサーバーの両方で発生します。こちらもご覧ください

私自身のラウンドライブラリを作成する以外に、これを回避する方法はありますか?


4
私は2.7.11ラウンド(5.59)のpythonでこれを試してみましたが、それは両方のWindowsとLinux x86の64ビットマシン、Cython(ドキュメントリンクが言及した私は推測する今、変更された)で5.6として結果を与えている?
アレックスPunnen

2
実際に正しく機能しないのはround(5.55, 1) = 5.5です。
ドミトリー

回答:


101

私はそれが格納される方法を助けることはできませんが、少なくともフォーマットは正しく機能します:

'%.1f' % round(n, 1) # Gives you '5.6'

11
私は試しましたprint '%.2f' % 655.665が、それは戻ってくる655.66はずです655.67
Liza

1
@カイリーは、stackoverflow.com / questions / 9301690 /…を参照してください。浮動小数点の不正確さはここのせいです-「5.665-> 5.67」、しかし「15.665-> 15.66」。正確な精度が必要な場合は、小数を使用してください。
ジミー

7
これは検索後に機能します:) from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN#浮動小数点の丸めに使用#浮動小数点のDecimal(str(655.665)).quantize(Decimal('1.11'), rounding=ROUND_HALF_UP)問題と制限
Liza

102

丸めを行わなくても、フォーマットは正しく機能します。

"%.1f" % n

18
docsによると、このスタイルの文字列フォーマットは最終的にはなくなるでしょう。新しいスタイルの形式は次のようになります"{:.1f}".format(n)
whereswalden

2
ラウンド正しくありません:'%.5f' % 0.988625与え0.98862
schlamar

@schlamar:これもround()の動作です。round(0.988625,5)も0.98862を返します。round(0.988626,5)および "%.5f"%0.988626は0.98863を与える
Vinko Vrsalovic

残念ながら "%.2f"%2.675は2.67を返します。これは、この方法を使用して2.68を期待している人にとっては予期しない回答になる可能性があります
Dion

30

Decimalモジュールを使用すると、「round」関数を使用せずに近似できます。これは、特に通貨アプリケーションを作成するときに、四捨五入に使用してきたものです。

Decimal(str(16.2)).quantize(Decimal('.01'), rounding=ROUND_UP)

これにより、16.20の10進数が返されます。


4
これは標準的な答えです –とにかく正確さが重要な場合、それはほとんどどこでもです。確かに、これは少し冗長です。しかし、その吸盤をヘルパー関数に入れれば、フォーマットして実行できます。
Cecil Curry、

2
rounding='ROUND_UP'
LMc

このエラーNameError: global name 'ROUND_UP' is not definedが発生した場合は、丸め関数をインポートする必要がありますfrom decimal import Decimal, ROUND_UPその他の丸め関数
スティーブンブレア

あなたの例はまだ危険に思われます:str()によって提供される丸めに依存しています。
YvesgereY

21

round(5.59, 1)正常に動作しています。問題は、5.6を2進浮動小数点で正確に表現できないことです。

>>> 5.6
5.5999999999999996
>>> 

Vinkoが言うように、文字列フォーマットを使用して、表示用の丸めを行うことができます。

Pythonには、必要に応じて小数演算用モジュールがあります


1
これは、Python 2.7またはPython 3.5のどちらでも問題ではありません
vy32


10

データ型を整数に切り替えることができます:

>>> n = 5.59
>>> int(n * 10) / 10.0
5.5
>>> int(n * 10 + 0.5)
56

次に、ロケールの小数点記号を挿入して数値を表示します。

しかし、ジミーの答えは良いです。


5

浮動小数点演算は、わずかではあるが煩わしい精度の不正確さに対して脆弱です。整数または固定小数点を使用できる場合は、精度が保証されます。


5

Decimalモジュールを見てください

10進数は、「人々を念頭に置いて設計された浮動小数点モデルに基づいており、必然的に最も重要な指針を備えています。コンピューターは、人々が学校で学ぶ算術と同じように機能する算術を提供する必要があります。」– 10進数演算仕様からの抜粋。

そして

10進数を正確に表すことができます。対照的に、1.1や2.2のような数値は、2進浮動小数点の正確な表現を持ちません。エンドユーザーは通常、バイナリ浮動小数点の場合のように1.1 + 2.2が3.3000000000000003と表示されることを期待しません。

Decimalは、浮動小数点演算を必要とするアプリを簡単に作成できるような種類の演算を提供し、それらの結果を人間が読める形式(会計など)で提示する必要あります。



4

それは確かに大きな問題です。このコードを試してください:

print "%.2f" % (round((2*4.4+3*5.6+3*4.4)/8,2),)

4.85と表示されます。それからあなたはします:

print "Media = %.1f" % (round((2*4.4+3*5.6+3*4.4)/8,1),)

そしてそれは4.8を示しています。手動で計算すると正確な答えは4.85ですが、次のようにすると:

print "Media = %.20f" % (round((2*4.4+3*5.6+3*4.4)/8,20),)

真実を確認できます。浮動小数点は、分母が2のべき乗である最も近い分数の有限和として格納されます。


3

%sprintfと同様の文字列フォーマット演算子を使用できます。

mystring = "%.2f" % 5.5999


2

私がやっている:

int(round( x , 0))

この場合、最初に単位レベルで適切に丸め、次に浮動小数点の印刷を避けるために整数に変換します。

そう

>>> int(round(5.59,0))
6

この答えは文字列のフォーマットよりもうまく機能すると思います。また、round関数を使用する方が感覚的になります。


2

round()この場合、私はまったく頼りにしません。検討する

print(round(61.295, 2))
print(round(1.295, 2))

出力されます

61.3
1.29

最も近い整数に丸める必要がある場合、これは望ましい出力ではありません。この動作を回避するには、次のようにしますmath.ceil()(またはmath.floor()切り捨てたい場合)。

from math import ceil
decimal_count = 2
print(ceil(61.295 * 10 ** decimal_count) / 10 ** decimal_count)
print(ceil(1.295 * 10 ** decimal_count) / 10 ** decimal_count)

出力

61.3
1.3

お役に立てば幸いです。


1

コード:

x1 = 5.63
x2 = 5.65
print(float('%.2f' % round(x1,1)))  # gives you '5.6'
print(float('%.2f' % round(x2,1)))  # gives you '5.7'

出力:

5.6
5.7

0

ここで私はラウンド失敗が見られます。これら2つの数値を小数点第1位で四捨五入したい場合はどうでしょうか。23.45 23.55私の教育では、これらを四捨五入して次の結果が得られるはずです。Pythonのround関数は単に5を切り捨てます。


1
あなたが話しているのは、「銀行の丸め」、つまり丸めを実行するさまざまな方法の1つです。
SimonMᶜKenzie2018

0

問題は、最後の桁が5の場合のみです。0.045は内部的に0.044999999999999として格納されます...単に最後の桁を6に増やし、四捨五入することができます。これにより、望ましい結果が得られます。

import re


def custom_round(num, precision=0):
    # Get the type of given number
    type_num = type(num)
    # If the given type is not a valid number type, raise TypeError
    if type_num not in [int, float, Decimal]:
        raise TypeError("type {} doesn't define __round__ method".format(type_num.__name__))
    # If passed number is int, there is no rounding off.
    if type_num == int:
        return num
    # Convert number to string.
    str_num = str(num).lower()
    # We will remove negative context from the number and add it back in the end
    negative_number = False
    if num < 0:
        negative_number = True
        str_num = str_num[1:]
    # If number is in format 1e-12 or 2e+13, we have to convert it to
    # to a string in standard decimal notation.
    if 'e-' in str_num:
        # For 1.23e-7, e_power = 7
        e_power = int(re.findall('e-[0-9]+', str_num)[0][2:])
        # For 1.23e-7, number = 123
        number = ''.join(str_num.split('e-')[0].split('.'))
        zeros = ''
        # Number of zeros = e_power - 1 = 6
        for i in range(e_power - 1):
            zeros = zeros + '0'
        # Scientific notation 1.23e-7 in regular decimal = 0.000000123
        str_num = '0.' + zeros + number
    if 'e+' in str_num:
        # For 1.23e+7, e_power = 7
        e_power = int(re.findall('e\+[0-9]+', str_num)[0][2:])
        # For 1.23e+7, number_characteristic = 1
        # characteristic is number left of decimal point.
        number_characteristic = str_num.split('e+')[0].split('.')[0]
        # For 1.23e+7, number_mantissa = 23
        # mantissa is number right of decimal point.
        number_mantissa = str_num.split('e+')[0].split('.')[1]
        # For 1.23e+7, number = 123
        number = number_characteristic + number_mantissa
        zeros = ''
        # Eg: for this condition = 1.23e+7
        if e_power >= len(number_mantissa):
            # Number of zeros = e_power - mantissa length = 5
            for i in range(e_power - len(number_mantissa)):
                zeros = zeros + '0'
            # Scientific notation 1.23e+7 in regular decimal = 12300000.0
            str_num = number + zeros + '.0'
        # Eg: for this condition = 1.23e+1
        if e_power < len(number_mantissa):
            # In this case, we only need to shift the decimal e_power digits to the right
            # So we just copy the digits from mantissa to characteristic and then remove
            # them from mantissa.
            for i in range(e_power):
                number_characteristic = number_characteristic + number_mantissa[i]
            number_mantissa = number_mantissa[i:]
            # Scientific notation 1.23e+1 in regular decimal = 12.3
            str_num = number_characteristic + '.' + number_mantissa
    # characteristic is number left of decimal point.
    characteristic_part = str_num.split('.')[0]
    # mantissa is number right of decimal point.
    mantissa_part = str_num.split('.')[1]
    # If number is supposed to be rounded to whole number,
    # check first decimal digit. If more than 5, return
    # characteristic + 1 else return characteristic
    if precision == 0:
        if mantissa_part and int(mantissa_part[0]) >= 5:
            return type_num(int(characteristic_part) + 1)
        return type_num(characteristic_part)
    # Get the precision of the given number.
    num_precision = len(mantissa_part)
    # Rounding off is done only if number precision is
    # greater than requested precision
    if num_precision <= precision:
        return num
    # Replace the last '5' with 6 so that rounding off returns desired results
    if str_num[-1] == '5':
        str_num = re.sub('5$', '6', str_num)
    result = round(type_num(str_num), precision)
    # If the number was negative, add negative context back
    if negative_number:
        result = result * -1
    return result

0

別の潜在的なオプションは次のとおりです。

def hard_round(number, decimal_places=0):
    """
    Function:
    - Rounds a float value to a specified number of decimal places
    - Fixes issues with floating point binary approximation rounding in python
    Requires:
    - `number`:
        - Type: int|float
        - What: The number to round
    Optional:
    - `decimal_places`:
        - Type: int 
        - What: The number of decimal places to round to
        - Default: 0
    Example:
    ```
    hard_round(5.6,1)
    ```
    """
    return int(number*(10**decimal_places)+0.5)/(10**decimal_places)

-4

何について:

round(n,1)+epsilon

これは、丸めが常にイプシロンによる丸め数からずれている場合にのみ機能します。場合はepsilon = .000001、その後はround(1.0/5.0, 1) + epsilon、正確な表現0.2を取ると、それ0.00001になるだろう。イプシロンがラウンド関数内にある場合、同様に悪い問題が発生します。
Michael Scott Cuthbert 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.