round () düzgün şekilde yuvarlanmıyor


123

Round () işlevi için dokümantasyon, ona bir sayı ilettiğinizi ve ondalıktan sonraki konumların yuvarlanacağını belirtir. Bu nedenle şunu yapmalıdır :

n = 5.59
round(n, 1) # 5.6

Ama gerçekte, kayan nokta tuhaflığı içeri girer ve şunu elde edersiniz:

5.5999999999999996

UI amaçları için görüntülemem gerekiyor 5.6. İnternette dolaştım ve bunun Python uygulamama bağlı olduğuna dair bazı belgeler buldum . Ne yazık ki, bu hem Windows geliştirici makinemde hem de denediğim her Linux sunucusunda meydana geliyor. Ayrıca buraya bakın .

Kendi yuvarlak kütüphanemi yaratmanın dışında, bunun etrafında herhangi bir yol var mı?


4
Ben piton 2.7.11 turda (5.59) ile bu denedim ve iki pencerelerde 5.6 ve Linux x86 64 bit makine, Cython (söz dokümantasyon bağlantı sanırım şimdi değiştirilir) olarak sonuç veriyor?
Alex Punnen

2
Gerçekte doğru çalışmadığı yer round(5.55, 1) = 5.5.
Dmitry

Yanıtlar:


102

Saklanma şekli konusunda yardımcı olamıyorum ama en azından biçimlendirme doğru çalışıyor:

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

11
denedim print '%.2f' % 655.665ama geri döndü 655.66, öyle olmalı655.67
Liza

1
@Kyrie bkz. Stackoverflow.com/questions/9301690/… . Kayan nokta yanlışlığı burada suçlanacak - "5.665 -> 5.67" ama "15.665 -> 15.66". Kesin hassasiyete ihtiyacınız varsa ondalık sayıları kullanın.
Jimmy

7
Bu arama :) sonra çalışıyor from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWNyüzen numaraları yuvarlama içerisinde kullanıma Decimal(str(655.665)).quantize(Decimal('1.11'), rounding=ROUND_HALF_UP)yüzen noktalarında # Sorunlar ve Sınırlamalar
Liza

102

Biçimlendirme, yuvarlamak zorunda kalmadan bile doğru şekilde çalışır:

"%.1f" % n

18
Dokümanlara göre , bu dize biçimlendirme stili eninde sonunda ortadan kalkacaktır. Yeni stil biçimi şu şekilde olacaktır"{:.1f}".format(n)
whereswalden

2
Doğru şekilde '%.5f' % 0.9886250.98862
yuvarlanmıyor

@schlamar: Bu aynı zamanda round () 'un davranışıdır: round (0.988625,5) da 0.98862 verir. yuvarlak (0.988626,5) ve "% .5f"% 0.988626 0.98863 verir
Vinko Vrsalovic

ne yazık ki "% .2f"% 2.675, 2.67 döndürecektir - bu, bu yöntemi kullanan ve 2.68 bekleyenler için beklenmedik bir cevap olabilir
Dion

30

Ondalık modülünü kullanırsanız, 'yuvarlak' işlevini kullanmadan yaklaştırabilirsiniz. Özellikle parasal uygulamalar yazarken yuvarlamak için kullandığım şey:

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

Bu, 16.20 olan bir Ondalık Sayı döndürecektir.


4
Bu kanonik cevaptır - her halükarda doğruluğun önemli olduğu ve hemen hemen her yerde olduğu yerde. Elbette: biraz ayrıntılı . Ama bu enayi bir yardımcı işleve atarsanız, biçimlendirmeniz ve gitmeniz iyi olur.
Cecil Curry

2
rounding='ROUND_UP'
LMc

Bu hatayı alırsanız NameError: global name 'ROUND_UP' is not definedsize yuvarlama fonksiyonu ithal etmek gerekir: from decimal import Decimal, ROUND_UP. Diğer yuvarlama işlevleri
Stephen Blair

Örneğiniz hala tehlikeli görünüyor: str () tarafından sağlanan yuvarlamaya güveniyorsunuz.
YvesgereY

21

round(5.59, 1)iyi çalışıyor. Sorun, 5.6'nın tam olarak ikili kayan noktada temsil edilememesidir.

>>> 5.6
5.5999999999999996
>>> 

Vinko'nun dediği gibi, görüntüleme için yuvarlama yapmak için dize biçimlendirmesini kullanabilirsiniz.

Python, ihtiyacınız varsa ondalık aritmetik için bir modüle sahiptir.


1
Bu artık Python 2.7 veya Python 3.5 için bir sorun değil
vy32


10

Veri türünü bir tam sayıya değiştirebilirsiniz:

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

Ve sonra yerelin ondalık ayırıcısını ekleyerek sayıyı görüntüleyin.

Ancak Jimmy'nin cevabı daha iyidir.


5

Kayan nokta matematiği, küçük, ancak sinir bozucu, hassasiyet yanlışlıklarına karşı savunmasızdır. Tamsayı veya sabit nokta ile çalışabilirseniz, kesinlik garanti edilir.


5

Decimal modülüne bir göz atın

Ondalık "insanlar göz önünde bulundurularak tasarlanmış bir kayan nokta modeline dayalıdır ve mutlaka yol gösterici çok önemli bir ilkeye sahiptir - bilgisayarlar, insanların okulda öğrendikleri aritmetik ile aynı şekilde çalışan bir aritmetik sağlamalıdır." - ondalık aritmetik spesifikasyondan alıntı.

ve

Ondalık sayılar tam olarak gösterilebilir. Buna karşılık, 1.1 ve 2.2 gibi sayılar, ikili kayan noktada tam bir gösterime sahip değildir. Son kullanıcılar tipik olarak 1.1 + 2.2'nin ikili kayan noktayla olduğu gibi 3.3000000000000003 olarak görüntülenmesini beklemezler.

Ondalık, kayan nokta işlemleri gerektiren uygulamaları yazmayı kolaylaştıran ve aynı zamanda bu sonuçları insan tarafından okunabilir bir formatta (örneğin, muhasebe) sunması gereken türden işlemler sağlar .



4

Bu gerçekten büyük bir problem. Bu kodu deneyin:

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

4,85 gösterir. O zaman yaparsın:

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

ve 4.8'i gösterir. Hesaplamaları elle mi yaparsınız, kesin cevap 4,85, ama denerseniz:

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

gerçeği görebilirsiniz: kayan nokta, paydaları ikinin üsleri olan en yakın sonlu kesirlerin toplamı olarak saklanır.


3

%Sprintf'e benzer şekilde dize biçimi operatörünü kullanabilirsiniz .

mystring = "%.2f" % 5.5999


2

Yapıyorum:

int(round( x , 0))

Bu durumda, önce birim seviyesinde düzgün bir şekilde yuvarlıyoruz, sonra bir float yazdırmamak için tam sayıya dönüştürüyoruz.

yani

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

Bu cevabın dizgiyi biçimlendirmekten daha iyi çalıştığını düşünüyorum ve ayrıca round işlevini kullanmak bana daha anlamlı geliyor.


2

round()Bu durumda hiç güvenmekten kaçınırdım . Düşünmek

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

çıktı verecek

61.3
1.29

En yakın tam sayıya tam yuvarlama yapmanız gerekiyorsa bu istenen bir çıktı değildir. Bu davranışı atlamak için math.ceil()(veya math.floor()aşağı yuvarlamak istiyorsanız) ile gidin:

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)

çıktılar

61.3
1.3

Umarım yardımcı olur.


1

Kod:

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'

Çıktı:

5.6
5.7

0

Burada yuvarlak hatayı görüyorum. Ya bu 2 sayıyı bir ondalık basamağa yuvarlamak isteseydiniz? 23.45 23.55 Benim eğitimim, bunları yuvarlayarak şunları elde etmeniz gerektiğiydi: 23.4 23.6 Önceki sayı tek ise yuvarlamanız gereken "kural", önceki sayı çift ise yuvarlama değil. Python'daki round işlevi 5'i kısaltır.


1
Bahsettiğiniz şey, yuvarlamayı gerçekleştirmenin birçok farklı yolundan biri olan "bankacıların yuvarlaması" dır .
Simon MᶜKenzie

0

Sorun yalnızca son rakam 5 olduğunda ortaya çıkar. Ör. 0,045, dahili olarak 0,044999999999999 olarak saklanır ... Son basamağı 6'ya çıkarabilir ve yuvarlayabilirsiniz. Bu size istenen sonuçları verecektir.

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

Diğer bir potansiyel seçenek ise:

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

Ne dersin:

round(n,1)+epsilon

Bu sadece, yuvarlama epsilon tarafından sürekli olarak yuvarlak sayıdan farklı olsaydı işe yarar. Eğer epsilon = .000001o zaman round(1.0/5.0, 1) + epsilonkesin bir temsilini 0,2 alıp 0,00001 yapar. Epsilon yuvarlak işlevin içinde olsaydı eşit derecede kötü sorunlar olurdu.
Michael Scott Cuthbert
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.