Python 3.x yuvarlama davranışı


176

Python 3.0'daki Yenilikler'i yeniden okuyordum ve şöyle diyor:

Round () işlevi yuvarlama stratejisi ve dönüş türü değişti. Kesin yarım vakalar şimdi sıfırdan ziyade en yakın çift sonuca yuvarlanıyor. (Örneğin, yuvarlak (2.5) artık 3 yerine 2 döndürüyor.)

ve tur için belgeler :

Round () özelliğini destekleyen yerleşik türler için, değerler güç eksi n'nin 10 en yakın katına yuvarlanır; iki kat eşit derecede yakınsa, yuvarlama eşit seçeneğe doğru yapılır

Yani, v2.7.3 altında :

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

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

beklediğim gibi. Ancak, şimdi v3.2.3 altında :

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

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

Bu, sezgisel ve yuvarlama hakkında anladığımın aksine (ve insanları gezmeye bağlı) görünüyor. İngilizce benim ana dilim değil ama bunu okuyana kadar yuvarlamanın ne anlama geldiğini bildiğimi düşündüm: - / Eminim v3 tanıtıldığında bunun tartışılması gerekiyordu, ama bunun için iyi bir neden bulamadım aramam.

  1. Bunun neden bu şekilde değiştirildiğini bilen var mı?
  2. Bu tür (benim için tutarsız) yuvarlama yapan başka ana programlama dilleri (örneğin, C, C ++, Java, Perl, ..) var mı?

Burada ne eksik?

GÜNCELLEME: @ Li-aungYip'in "Banker yuvarlama" yorumu yeniden bana doğru arama terimi / anahtar kelimeler verdi ve ben bu SO soru bulundu: .NET neden banker yuvarlama varsayılan olarak kullanıyor? , bu yüzden dikkatlice okuyacağım.


28
Buna bakacak vaktim yok, ama buna "Banker yuvarlaması" denildiğine inanıyorum. Finans sektöründe yaygın olduğuna inanıyorum.
Li-aung Yip

2
@sberry iyi, evet, davranışı kendi tanımıyla tutarlıdır. Eğer "yuvarlama" değerini iki katına çıkarır ve yapsaydı, tutarlı olur :) .. ama yuvarlamanın yaygın olarak ne anlama geldiğine aykırı görünüyor . Bu yüzden daha iyi bir anlayış arıyorum.
Levon

1
@ Li-aungYip Öncü "Banker yuvarlama" için teşekkürler .. Ben bakacağım.
Levon


3
Sadece bir not: Bankacılar yuvarlama sadece finansta yaygın değildir. Ben zaten 70'li yıllarda ilkokulda bu şekilde öğretildi :-)
Lennart Regebro

Yanıtlar:


160

Python 3.0'ın yolu bugünlerde standart yuvarlama yöntemi olarak kabul ediliyor, ancak bazı dil uygulamaları henüz otobüste değil.

Basit "her zaman 0,5 yukarı yuvarla" tekniği, yüksek sayıya doğru hafif bir sapmaya neden olur. Çok sayıda hesaplamada, bu önemli olabilir. Python 3.0 yaklaşımı bu sorunu ortadan kaldırır.

Ortak kullanımda birden fazla yuvarlama yöntemi vardır. Kayan noktalı matematik için uluslararası standart olan IEEE 754, beş farklı yuvarlama yöntemi tanımlar (Python 3.0 tarafından kullanılan yöntem varsayılan değerdir). Ve başkaları da var.

Bu davranış olması gerektiği kadar yaygın olarak bilinmemektedir. AppleScript, doğru hatırlıyorsam, bu yuvarlama yöntemini erken benimseyen biriydi. roundAppleScript komut aslında teklif çeşitli seçenekler yapar, ancak IEEE 754'e olduğu gibi yuvarlak doğru bile Görünüşe uygulanan mühendis varsayılan roundkomut böylece tüm istekleri ile bıkmış "Ben öğrenilen gibi çalışması ekledi: " round 2.5 rounding as taught in schoolgeçerli bir AppleScript komutudur. :-)


4
Bu "varsayılan standart yuvarlama yöntemi hemen hemen evrensel olarak bu gün" farkında değildi, (ya da başkası) aynı şekilde yuvarlama uygulamak C / C ++ / Java / Perl veya başka bir "ana akış" dilleri biliyor musunuz?
Levon

3
Ruby yapar. Microsoft'un .NET dilleri bunu yapar. Yine de Java görünmüyor. Mümkün olan her dil için bunu izleyemiyorum, ancak sanırım oldukça yeni tasarlanmış dillerde yaygın. C ve C ++ onların yeterince yaşlı olduğunu hayal ediyorum.
tür

5
yakut döner 3için2.5.round
jfs

14
AppleScript'in bunu ele alması hakkında biraz ekledim çünkü "eski" davranışın uygulandığı alaycı yolu seviyorum.
tür

2
@kindall Bu yöntem 1985'ten beri IEEE varsayılan yuvarlama modudur (IEEE 754-1985 yayınlandığında). En azından C89'dan (ve dolayısıyla C ++ da) beri C'deki varsayılan yuvarlama modu olmuştur, ancak C99 (ve bundan önce sporadik destekli C ++ 11) olduğundan, bir "round ()" işlevi kullanan bağları sıfıra yuvarlanır. Dahili kayan nokta yuvarlama ve rint () işlev ailesi hala yuvarlama bağlarını varsayılan olarak ayarlayan yuvarlama modu ayarına uymaya devam eder.
Wlerin

41

Ondalık modülünü kullanarak Py3000'de aldığınız yuvarlamayı kontrol edebilirsiniz :

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

Teşekkürler .. Bu modüle aşina değildim. Python v 2.x davranışını nasıl alacağım hakkında herhangi bir fikir? Gösterdiğiniz örnekler böyle görünmüyor. Sadece bunun mümkün olup olmadığını merak ettim.
Levon

1
@Levon: Sabit ROUND_HALF_UP, Python 2.X'in eski davranışıyla aynı.
dawg

2
Ondalık modül için bunu dolaylı olarak yapan bir bağlam da ayarlayabilirsiniz. setcontext()Fonksiyona bakın .
tür

Bugün tam da aradığım şey buydu. Python'da beklendiği gibi çalışmak 3.4.3. Ayrıca kayda değer, bunu değiştirerek mermi ne kadar kontrol edebilirsiniz quantize(decimal.Decimal('1')için quantize(decimal.Decimal('0.00')böyle para olarak en yakın 100s turda istiyorum.
Igor

Bu çözüm , pozitif round(number, ndigits)olduğu sürece bir yedek olarak çalışır ndigits, ancak can sıkıcı bir şekilde bunun yerine bir şey kullanamazsınız round(5, -1).
Pekka Klärck

15

Buraya dokümantasyondan önemli bir not eklemek için:

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

Not

Şamandıralar için round () davranışı şaşırtıcı olabilir: örneğin, round (2.675, 2) beklenen 2.68 yerine 2.67 verir. Bu bir hata değildir: ondalık kesirlerin çoğunun tam olarak bir şamandıra olarak temsil edilememesi sonucudur. Daha fazla bilgi için bkz. Kayan Nokta Aritmetiği: Sorunlar ve Sınırlamalar.

Python 3.2'de aşağıdaki sonuçları almak sizi şaşırtmasın:

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

Bunu gördüm. Ve benim ilk tepkim: "2.67x" in tüm permütasyonlarını temsil edemeyen 16 bitlik bir CPU kim kullanıyor? Kesirlerin şamandıra içinde ifade edilemediğini söylemek burada bir günah keçisi gibi görünmüyor: modern bir CPU, HERHANGİ bir dilde (Python hariç) yanlış değil
Adam

9
@Adam: Sanırım yanlış anlıyorsun. Şamandıraları depolamak için kullanılan ikili biçim (IEEE 754 binary64) 2.675tam olarak temsil edilemez : bilgisayarın alabileceği en yakın şey 2.67499999999999982236431605997495353221893310546875. Bu oldukça yakın, ama değil tam eşit 2.675: bu kadar çok az daha yakın 2.67daha 2.68. Böylece roundişlev doğru olanı yapar ve noktadan sonraki 2 basamaktan daha yakın bir değere yuvarlar 2.67. Bunun Python ile hiçbir ilgisi yoktur ve ikili kayan nokta ile ilgisi vardır.
Mark Dickinson

3
Bu "doğru olan" değil, çünkü kaynak kodu sabiti verildi :), ama anlıyorum.
Adam

@Adam: Daha önce JS'de aynı quirkiness ile karşılaştım, bu yüzden dile özgü değil.
Igor

5

Son zamanlarda bununla ilgili problemlerim de vardı. Bu nedenle, bunu ele alan ve aynı yuvarlama davranışını veren 2 fonksiyon trueround () ve trueround_precision () içeren bir python 3 modülü geliştirdim (ilkokuldan değil bankacının yuvarlanması). İşte modül. Kodu kaydedin ve kopyalayın veya içe aktarın. Not: trueround_precision modülü, ondalık modüldeki ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP ve ROUND_05UP bayraklarına göre ihtiyaca göre yuvarlama davranışını değiştirebilir (daha fazla bilgi için bu modüllere bakın). Aşağıdaki işlevler için docstringlere bakın veya daha fazla dokümantasyon için bir tercümana kopyalandıysa yardım (trueround) ve yardım (trueround_precision) kullanın.

#! /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)

Bu yardımcı olur umarım,

Narnie


5

Python 3.x, .5 değerlerini bile olan bir komşuya yuvarlar

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

ancak, gerekirse ondalık yuvarlama "geri" her zaman .5 yukarı yuvarlanabilir:

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'te Python 2 yuvarlama davranışı.

Ondalık basamaklara 1 ekleme. 15 basamağa kadar doğruluk.

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

3
Bu formülün arkasındaki sezgiyi açıklayabilir misiniz?
Hadi

2
Anladığım kadarıyla, doğru bir şekilde temsil edilemeyen kesirlerde 15 9'a kadar, daha sonra kesinsizlik olacaktır. Örneğin, 2.675bir 2.67499999999999982236431605997495353221893310546875. 1e-15 eklendiğinde 2.675'in üzerine eğilir ve doğru şekilde yuvarlanır. kesir zaten kod sabitinin üzerindeyse, 1e-15 eklenmesi yuvarlamaya hiçbir şey değiştirmez.
Benoit Dufresne

1

Bazı durumlar:

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

Düzeltmek için:

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

Daha fazla ondalık basamak istiyorsanız, örneğin 4, (+ 0.0000001) eklemeniz gerekir.

Benim için çalış.


Bu benim için işe yarayan tek çözümdü, gönderi için teşekkürler. Herkes 0.5 yukarı / aşağı yuvarlama niyetinde gibi görünüyor, bu yüzden çoklu ondalık yuvarlama sorunlarını yönetemedim.
Gayathri

-1

Örnek Çoğaltma:

['{} => {}'.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

Devletler:

Ondalık noktadan sonra rakamın hassasiyetine yuvarlanan dönüş sayısı. Ndigits atlanırsa veya Yok ise, girişine en yakın tamsayıyı döndürür.

Round () özelliğini destekleyen yerleşik türler için, değerler güç eksi ndigit değerlerine 10'un en yakın katına yuvarlanır; iki kat eşit olarak yakınsa, yuvarlama eşit seçeneğe doğru yapılır (örneğin, hem yuvarlak (0.5) hem de yuvarlak (-0.5) 0'dır ve yuvarlak (1.5) 2'dir). Herhangi bir tam sayı değeri ndigits (pozitif, sıfır veya negatif) için geçerlidir. Ndigits atlanırsa veya Yok ise, döndürülen değer bir tamsayıdır. Aksi takdirde, dönüş değeri sayı ile aynı türdedir.

Genel bir Python nesne numarası için delegeleri sayıya yuvarlayın. yuvarlak .

Not Şamandıralar için round () davranışı şaşırtıcı olabilir: örneğin, round (2.675, 2) beklenen 2.68 yerine 2.67 verir. Bu bir hata değildir: ondalık kesirlerin çoğunun tam olarak bir şamandıra olarak temsil edilememesi sonucudur. Daha fazla bilgi için bkz. Kayan Nokta Aritmetiği: Sorunlar ve Sınırlamalar.

Bu içgörü göz önüne alındığında, onu çözmek için biraz matematik kullanabilirsiniz

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

şimdi aynı testi round yerine my_round ile çalıştırabilirsiniz.

['{} => {}'.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

Okulda öğretildiği gibi Python 3.x'te yuvarlamanın en kolay yolu yardımcı bir değişken kullanmaktır:

n = 0.1 
round(2.5 + n)

Ve bunlar 2.0 ila 3.0 serisinin sonuçları olacak (0.1 adımda):

>>> 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 modülünü kullanarak yuvarlamanızı kontrol edebilirsiniz:

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

Bu her zaman ondalık kısmı olmadan sayıyı döndürür, bu yuvarlama değildir. tavan (2.5) = 2,
tavan

1
python3 + 'da, sayı bağımsız değişkeni pozitif veya negatif bir sayıysa, tavan işlevi tavan değerini döndürür.
Eds_k

[14]: math.ceil (2.99) Çıkan [14]: 3
Eds_k

Evet, yanıldığım için üzgünüm. Ceil (), tavan değerini döndürürken floor (), bahsettiğim değeri döndürür. Ama yine de, bence bu oldukça yuvarlama davranışı değil (her iki işlev de)
krafter

-4

Bu kodu deneyin:

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

Sonuç:

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

Çıkışı buradan kontrol edebilirsiniz: https://i.stack.imgur.com/QQUkS.png

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.