web-dev-qa-db-ja.com

Python 3.xの丸め動作

私はちょうど読み直していました Python 3.0 の新機能には次のように書かれています:

Round()関数の丸め戦略と戻り値の型が変更されました。正確な中間ケースは、ゼロから離れるのではなく、最も近い偶数の結果に丸められるようになりました。 (たとえば、round(2.5)は3ではなく2を返すようになりました。)

および round のドキュメント:

Round()をサポートする組み込み型の場合、値は10のマイナスnに最も近い倍数に丸められます。 2つの倍数が等しく近い場合、偶数の選択に向かって丸めが行われます。

したがって、v2.7.3

In [85]: round(2.5)
Out[85]: 3.0

In [86]: round(3.5)
Out[86]: 4.0

私が期待していたように。ただし、今v3.2.3の下:

In [32]: round(2.5)
Out[32]: 2

In [33]: round(3.5)
Out[33]: 4

これは直観に反するようで、私が丸めについて理解している(そして人をつまずかせることにバインドされている)ことに反しています。英語は私の母国語ではありませんが、これを読むまで、丸めが何を意味するのか知っていたと思いました:-/ v3が導入されたとき、これについての議論があったに違いないと思いますが、私の検索。

  1. これがなぜこれに変更されたのか、誰にも洞察がありますか?
  2. このような(一貫性のない)丸めを行う他の主流のプログラミング言語(例えば、C、C++、Java、Perl、..)はありますか?

ここで何が欠けていますか?

更新:@ Li-aungYipのコメント「Banker's rounding」は、検索するための正しい検索語/キーワードを提供してくれました。このSO質問を見つけました: 。 ? なので、注意深く読んでいきます。

148
Levon

Python 3.0の方法は、最近では標準の丸め方法と見なされていますが、一部の言語実装はまだバス上にありません。

単純な「常に0.5に切り上げる」手法では、高い数値に向かってわずかに偏りが生じます。多数の計算では、これは重要です。 Python 3.0アプローチは、この問題を排除します。

一般的に使用される丸め方法は複数あります。浮動小数点演算の国際標準であるIEEE 754では、 5つの異なる丸め方法 (Python 3.0で使用されるものがデフォルトです)を定義しています。他にもあります。

この動作は、本来あるべきほど広く知られていません。 AppleScriptは、私が正しく覚えていれば、この丸め方法の早期採用者でした。 AppleScriptのroundコマンドには実際にいくつかのオプションがありますが、IEEE 754のように偶数に丸めるのがデフォルトです。明らかにroundコマンドを実装したエンジニアは、 「学校で学んだように機能させる」ことを要求し、彼はそれを実装しました:round 2.5 rounding as taught in schoolは有効なAppleScriptコマンドです。 :-)

134
kindall

Decimalモジュール を使用して、Py3000で取得する丸めを制御できます。

>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'), 
    rounding=decimal.ROUND_HALF_UP)
>>> Decimal('4')

>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'),    
    rounding=decimal.ROUND_HALF_EVEN)
>>> Decimal('2')

>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'), 
    rounding=decimal.ROUND_HALF_DOWN)
>>> Decimal('3')
33
dawg

ここにドキュメントから重要なメモを追加するだけです:

https://docs.python.org/dev/library/functions.html#round

注意

Floatに対するround()の動作は驚くべきものです。たとえば、round(2.675、2)は、予想される2.68ではなく2.67を返します。これはバグではありません。これは、ほとんどの小数部が浮動小数点数として正確に表現できないという事実の結果です。詳細については、浮動小数点演算:問題と制限を参照してください。

したがって、Python 3.2で次の結果が得られても驚かないでください。

>>> round(0.25,1), round(0.35,1), round(0.45,1), round(0.55,1)
(0.2, 0.3, 0.5, 0.6)

>>> round(0.025,2), round(0.035,2), round(0.045,2), round(0.055,2)
(0.03, 0.04, 0.04, 0.06)
10
skif1979

私も最近これに問題がありました。したがって、私はこれに対処し、同じ丸め動作を与える2つの関数trueround()およびtrueround_precision()を使用するpython 3モジュールを開発しました(銀行家の丸めではなく)。これがモジュールです。コードを保存してコピーするかインポートするだけです。注:trueround_precisionモジュールは、10進数モジュールのROUND_CEILING、ROUND_DOWN、ROUND_FLOOR、ROUND_HALF_DOWN、ROUND_HALF_EVEN、ROUND_HALF_UP、ROUND_UP、およびROUND_05UPフラグに従って、必要に応じて丸め動作を変更できます(詳細については、モジュールのドキュメントを参照してください)。以下の関数については、docstringsを参照するか、ヘルプ(trueround)およびhelp(trueround_precision)を使用して、さらにドキュメントを作成するためにインタープリターにコピーします。

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

def trueround(number, places=0):
    '''
    trueround(number, places)

    example:

        >>> trueround(2.55, 1) == 2.6
        True

    uses standard functions with no import to give "normal" behavior to 
    rounding so that trueround(2.5) == 3, trueround(3.5) == 4, 
    trueround(4.5) == 5, etc. Use with caution, however. This still has 
    the same problem with floating point math. The return object will 
    be type int if places=0 or a float if places=>1.

    number is the floating point number needed rounding

    places is the number of decimal places to round to with '0' as the
        default which will actually return our interger. Otherwise, a
        floating point will be returned to the given decimal place.

    Note:   Use trueround_precision() if true precision with
            floats is needed

    GPL 2.0
    copywrite by Narnie Harshoe <[email protected]>
    '''
    place = 10**(places)
    rounded = (int(number*place + 0.5if number>=0 else -0.5))/place
    if rounded == int(rounded):
        rounded = int(rounded)
    return rounded

def trueround_precision(number, places=0, rounding=None):
    '''
    trueround_precision(number, places, rounding=ROUND_HALF_UP)

    Uses true precision for floating numbers using the 'decimal' module in
    python and assumes the module has already been imported before calling
    this function. The return object is of type Decimal.

    All rounding options are available from the decimal module including 
    ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, 
    ROUND_HALF_UP, ROUND_UP, and ROUND_05UP.

    examples:

        >>> trueround(2.5, 0) == Decimal('3')
        True
        >>> trueround(2.5, 0, ROUND_DOWN) == Decimal('2')
        True

    number is a floating point number or a string type containing a number on 
        on which to be acted.

    places is the number of decimal places to round to with '0' as the default.

    Note:   if type float is passed as the first argument to the function, it
            will first be converted to a str type for correct rounding.

    GPL 2.0
    copywrite by Narnie Harshoe <[email protected]>
    '''
    from decimal import Decimal as dec
    from decimal import ROUND_HALF_UP
    from decimal import ROUND_CEILING
    from decimal import ROUND_DOWN
    from decimal import ROUND_FLOOR
    from decimal import ROUND_HALF_DOWN
    from decimal import ROUND_HALF_EVEN
    from decimal import ROUND_UP
    from decimal import ROUND_05UP

    if type(number) == type(float()):
        number = str(number)
    if rounding == None:
        rounding = ROUND_HALF_UP
    place = '1.'
    for i in range(places):
        place = ''.join([place, '0'])
    return dec(number).quantize(dec(place), rounding=rounding)

お役に立てれば、

ナーニー

6
narnie

Python 3.xは、.5値を隣接する偶数に丸めます

assert round(0.5) == 0
assert round(1.5) == 2
assert round(2.5) == 2

import decimal

assert decimal.Decimal('0.5').to_integral_value() == 0
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 2

ただし、必要に応じて、10進数の「バック」を常に0.5に切り上げるように変更できます。

decimal.getcontext().rounding = decimal.ROUND_HALF_UP

assert decimal.Decimal('0.5').to_integral_value() == 1
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 3

i = int(decimal.Decimal('2.5').to_integral_value()) # to get an int
assert i == 3
assert type(i) is int
3
kares

ある場合:

in: Decimal(75.29 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(75.29 / 2, 2)
out: 37.65 GOOD

in: Decimal(85.55 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(85.55 / 2, 2)
out: 42.77 BAD

修正のため:

in: round(75.29 / 2 + 0.00001, 2)
out: 37.65 GOOD
in: round(85.55 / 2 + 0.00001, 2)
out: 42.78 GOOD

4のようにさらに小数が必要な場合は、(+ 0.0000001)を追加する必要があります。

私のために働きます。

1
Virako

pythonでのPython 2の丸め動作3。

小数点第15位に1を追加します。 15桁までの精度。

round2=lambda x,y=None: round(x+1e-15,y)
1
SmartManoj

サンプルの再現:

['{} => {}'.format(x+0.5, round(x+0.5)) for x in range(10)]

['0.5 => 0', '1.5 => 2', '2.5 => 2', '3.5 => 4', '4.5 => 4', '5.5 => 6', '6.5 => 6', '7.5 => 8', '8.5 => 8', '9.5 => 10']

API: https://docs.python.org/3/library/functions.html#round

州:

小数点以下n桁の精度に丸められた数値を返します。 ndigitsが省略されるかNoneの場合、入力に最も近い整数を返します。

Round()をサポートする組み込み型の場合、値は10のべき乗からndigitsを引いた値の最も近い倍数に丸められます。 2つの倍数が等しく近い場合、偶数の選択に向かって丸めが行われます(たとえば、round(0.5)とround(-0.5)は両方とも0で、round(1.5)は2です)。 ndigitsには任意の整数値が有効です(正、ゼロ、または負)。 ndigitsが省略されている場合、またはNoneの場合、戻り値は整数です。それ以外の場合、戻り値は数値と同じ型を持ちます。

一般的なPythonオブジェクト番号の場合、デリゲートを数値に丸めます。round

注floatに対するround()の動作は驚くべきものです。たとえば、round(2.675、2)は、予想される2.68ではなく2.67を返します。これはバグではありません。これは、ほとんどの小数部が浮動小数点数として正確に表現できないという事実の結果です。詳細については、浮動小数点演算:問題と制限を参照してください。

この洞察を考えると、あなたはそれを解決するためにいくつかの数学を使用することができます

import math
def my_round(i):
  f = math.floor(i)
  return f if i - f < 0.5 else f+1

これで、ラウンドではなくmy_roundで同じテストを実行できます。

['{} => {}'.format(x + 0.5, my_round(x+0.5)) for x in range(10)]
['0.5 => 1', '1.5 => 2', '2.5 => 3', '3.5 => 4', '4.5 => 5', '5.5 => 6', '6.5 => 7', '7.5 => 8', '8.5 => 9', '9.5 => 10']
0
Fallenreaper

round演算子は、値を最も近い整数値に丸めます。

値がo.5より大きい場合、1に四捨五入されます

print(round(211.5554, 2)) // output is 211.56

値が0.5より小さい場合、0に四捨五入されます

print(round(211.5544, 2)) // output is 211.55
0
Asad Manzoor