Python 3.xの丸め動作


176

私はちょうど再読み込みされた新機能ではPython 3.0を、それが述べて:

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

ラウンドのドキュメント:

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の「バンカーの丸め」に関するコメントで、検索する適切な検索用語/キーワードが与えられ、このSOの質問が見つかりました。なぜ.NETはデフォルトで銀行の丸めを使用するのですか?なので、注意深く読んでいきます。


27
これを調べる時間はありませんが、これは「バンカーの丸め」と呼ばれていると思います。金融業界ではよくあることだと思います。
Li-aung Yip

2
@sberryまあ、はい、その動作は独自の説明と一致しています。したがって、「丸め」がその値を2倍にしてそれを行ったと言う場合も、一貫性があります:) ..しかし、丸めが一般的に意味するものに反しているようです。だから私はより良い理解を探しています。
Levon、2012年

1
@ Li-aungYip「バンカーの丸め」のリードをありがとう..調べます。
Levon、2012年


3
ただ注意:銀行だけの丸めは、金融だけでは一般的ではありません。これは、70年代にすでに小学校で回るように教えられた方法です:-)
Lennart Regebro 2013年

回答:


160

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

単純な「常に0.5に切り上げる」手法では、数値が大きくなる方向にわずかにバイアスがかかります。大量の計算では、これは重要になる可能性があります。Python 3.0のアプローチは、この問題を排除します。

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

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


4
私はこの「最近のデフォルトの標準的な丸め方法」にほとんど気づいていませんでした。C/ C ++ / Java / Perlまたは他の「メインストリーム」言語が同じ方法で丸めを実装しているかどうかを(または他の誰かが)知っていますか?
Levon、2012年

3
Rubyはそれを行います。Microsoftの.NET言語がそれを行います。ただし、Javaは表示されません。可能なすべての言語でそれを追跡することはできませんが、かなり最近に設計された言語で最も一般的だと思います。CとC ++は十分に古く、そうでないと思います。
キンドール

5
rubyが返さ3れる2.5.round
jfs 2012年

14
「古い」動作が実装されている皮肉な方法が好きなので、AppleScriptによるこの処理について少し追加しました。
キンドール

2
@kindallこのメソッドは、1985年(IEEE 754-1985が公開されたとき)からIEEEのデフォルトの丸めモードになっています。これは、少なくともC89以降(したがってC ++でも)、Cのデフォルトの丸めモードでもあります、C99(およびその前に散発的にサポートされているC ++ 11)以降、使用する「round()」関数が使用可能になりました代わりに、タイはゼロから離れて丸めます。内部浮動小数点の丸めと関数のrint()ファミリーは、丸めモード設定に引き続き従います。
Wlerin 2015年

41

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')

ありがとう..私はこのモジュールに慣れていませんでした。どのようにしてPython v 2.xの動作を取得するのですか?あなたが示す例はそれをしていないようです。それが可能かどうか気になるところです。
Levon、2012年

1
@Levon:定数ROUND_HALF_UPはPython 2.Xの以前の動作と同じです。
-dawg

2
暗黙的にこれを行うDecimalモジュールのコンテキストを設定することもできます。setcontext()関数を参照してください。
キンドール

これはまさに私が今日探していたものです。Python 3.4.3で期待どおりに動作します。また注目に値する、あなたはそれが変更することにより、丸めどのくらい制御することができますquantize(decimal.Decimal('1')quantize(decimal.Decimal('0.00')あなたがそのようなお金のためとして最も近い100Sにラウンドしたい場合。
イゴール

このソリューションは、ポジティブであるround(number, ndigits)限りの代替品として機能しndigitsますが、厄介なことに、このソリューションを使用してのようなものを置き換えることはできませんround(5, -1)
PekkaKlärck18年

15

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

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)

私はそれを観た。そして、私の最初の反応:「2.67x」のすべての順列を表すことができない16ビットCPUを誰が使用していますか?分数は浮動小数点数で表現できないと言うと、ここではスケープゴートのように見えます。現代のCPUは、どの言語でも(Pythonを除いて)不正確です
Adam

9
@アダム:私はあなたが誤解していると思います。浮動小数点数を格納するために使用されるバイナリ形式(IEEE 754 binary64)は、2.675正確に表すことはできません2.67499999999999982236431605997495353221893310546875。それは非常に近いですが、そうではありません正確に等しい2.675:それは非常に少し近い2.67よりも2.68。したがって、round関数は正しいことを行い、それを2桁後のより近い値、つまりに丸めます2.67。これはPythonとは関係なく、2進浮動小数点とすべて関係があります。
マークディキンソン

3
ソースコード定数が与えられたので、それは「正しいこと」ではありません:)しかし、私はあなたの要点を理解しています。
アダム

@Adam:以前にJSでこれと同じ癖に出くわしたので、言語固有ではありません。
イゴール

5

私も最近これに問題がありました。したがって、私はこれに対処し、同じ丸め動作を与える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を参照するか、詳細なドキュメントのためにインタプリタにコピーする場合は、help(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 <signupnarnie@gmail.com>
    '''
    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 <signupnarnie@gmail.com>
    '''
    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)

お役に立てれば、

ナニー


5

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

ただし、必要に応じて、小数点以下の四捨五入「バック」を常に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

1

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

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

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

3
この公式の背後にある直感について説明していただけますか?
Hadi 2017

2
私が理解していることから、正確に表現できない分数は、最大15の9があり、それから不正確になります。たとえば2.675です2.67499999999999982236431605997495353221893310546875。1e-15を追加すると、2.675を超え、正しく丸められます。分数がすでにコード定数を超えている場合、1e-15を追加しても丸めは変わりません。
Benoit Dufresne

1

ある場合:

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)を追加する必要があります。

私のために働きなさい。


これは私に役立つ唯一の解決策でした、投稿してくれてありがとう。誰もが0.5の切り上げ/切り下げに熱心であるように思われるので、私はマルチ10進数の丸めの問題を管理できませんでした。
ガヤトリ

-1

サンプルの再現:

['{} => {}'.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の累乗からn桁を引いた最も近い倍数に丸められます。2つの倍数が等しく近い場合、丸めは偶数の選択に向かって行われます(たとえば、round(0.5)とround(-0.5)は両方とも0であり、round(1.5)は2です)。任意の整数値がn桁(正、ゼロ、または負)に有効です。ndigitsが省略されている場合、またはNoneの場合、戻り値は整数です。それ以外の場合、戻り値の型は数値と同じです。

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

注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']

-2

学校で教えられているようにPython 3.xで丸める最も簡単な方法は、補助変数を使用することです。

n = 0.1 
round(2.5 + n)

そして、これらはシリーズ2.0から3.0(0.1ステップ)の結果になります。

>>> round(2 + n)
>>> 2

>>> round(2.1 + n)
>>> 2

>>> round(2.2 + n)
>>> 2

>>> round(2.3 + n)
>>> 2

>>> round(2.4 + n)
>>> 2

>>> round(2.5 + n)
>>> 3

>>> round(2.6 + n)
>>> 3

>>> round(2.7 + n)
>>> 3

>>> round(2.8 + n)
>>> 3

>>> round(2.9 + n)
>>> 3

>>> round(3 + n)
>>> 3

-2

math.ceilモジュールを使用して、丸めを制御できます。

import math
print(math.ceil(2.5))
> 3

それは常に小数部なしで数を返します、これは丸めではありません。ceil(2.5)= 2、ceil(2.99)= 2
クロフター

1
python3 +では、number引数が正または負の数の場合、ceil関数は上限値を返します。
Eds_k

In [14]:math.ceil(2.99)Out [14]:3
Eds_k

はい、申し訳ありませんでした。Ceil()は上限値を返し、floor()は私が話していた値を返します。しかし、それでも、私の意見では、これは丸め動作ではありません(これらの関数の両方)
krafter

-4

このコードを試してください:

def roundup(input):   
   demo = input  if str(input)[-1] != "5" else str(input).replace("5","6")
   place = len(demo.split(".")[1])-1
   return(round(float(demo),place))

結果は次のようになります。

>>> x = roundup(2.5)
>>> x
3.0  
>>> x = roundup(2.05)
>>> x
2.1 
>>> x = roundup(2.005)
>>> x
2.01 

ここで確認できるOoutput:https ://i.stack.imgur.com/QQUkS.png

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.