round () tampaknya tidak membulatkan dengan benar

123

Dokumentasi untuk fungsi round () menyatakan bahwa Anda memberikan angka, dan posisi melewati desimal untuk membulatkan. Jadi harus melakukan ini:

n = 5.59
round(n, 1) # 5.6

Tapi, pada kenyataannya, keanehan floating point tua yang bagus merayap masuk dan Anda mendapatkan:

5.5999999999999996

Untuk keperluan UI, saya perlu menampilkan 5.6. Saya mencari-cari di Internet dan menemukan beberapa dokumentasi yang bergantung pada implementasi Python saya. Sayangnya, ini terjadi pada mesin pengembang Windows saya dan setiap server Linux yang pernah saya coba. Lihat di sini juga .

Singkatnya membuat perpustakaan bundar saya sendiri, apakah ada cara lain?

swilliams
sumber
4
Saya mencoba ini dengan python 2.7.11 round (5.59) dan ini memberikan hasil 5.6 di kedua windows dan linux x86 mesin 64 bit, Cython? (Link dokumentasi yang disebutkan sekarang saya kira berubah)
Alex Punnen
2
Di mana sebenarnya tidak berfungsi dengan benar adalah round(5.55, 1) = 5.5.
Dmitry

Jawaban:

102

Saya tidak dapat membantu cara penyimpanannya, tetapi setidaknya pemformatan berfungsi dengan benar:

'%.1f' % round(n, 1) # Gives you '5.6'
Jimmy
sumber
11
saya mencoba print '%.2f' % 655.665tetapi itu kembali 655.66, seharusnya655.67
Liza
1
@Kyrie melihat stackoverflow.com/questions/9301690/... . Ketidakakuratan floating point yang harus disalahkan di sini - "5.665 -> 5.67" tetapi "15.665 -> 15.66". Gunakan desimal jika Anda membutuhkan ketepatan yang tepat.
Jimmy
7
ini berfungsi setelah mencari :) from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN# gunakan dalam pembulatan angka mengambang Decimal(str(655.665)).quantize(Decimal('1.11'), rounding=ROUND_HALF_UP)# Masalah dan Batasan dalam floating point
Liza
102

Pemformatan bekerja dengan benar bahkan tanpa harus dibulatkan:

"%.1f" % n
Vinko Vrsalovic
sumber
18
Menurut dokumen , gaya pemformatan string ini pada akhirnya akan hilang. Format gaya baru akan menjadi"{:.1f}".format(n)
tanggal
2
Tidak membulatkan dengan benar: '%.5f' % 0.988625memberi0.98862
schlamar
@schlamar: Itu juga perilaku round (): round (0,988625,5) juga menghasilkan 0,98862. round (0.988626,5) serta "% .5f"% 0.988626 menghasilkan 0.98863
Vinko Vrsalovic
sayangnya "% .2f"% 2.675 akan mengembalikan 2.67 - yang mungkin merupakan jawaban tak terduga bagi mereka yang menggunakan metode ini dan mengharapkan 2.68
Dion
30

Jika Anda menggunakan modul Desimal, Anda dapat memperkirakan tanpa menggunakan fungsi 'bulat'. Inilah yang telah saya gunakan untuk pembulatan terutama saat menulis aplikasi moneter:

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

Ini akan mengembalikan Angka Desimal yaitu 16.20.

Robert Griesmeyer
sumber
4
Ini adalah jawaban kanonik - di mana keakuratan penting, bagaimanapun, yang cukup banyak di mana-mana. Tentu: ini agak bertele-tele . Tapi buang pengisap itu dalam fungsi pembantu dan Anda baik untuk memformat dan pergi.
Cecil Curry
2
rounding='ROUND_UP'
LMc
Jika Anda mendapatkan kesalahan ini NameError: global name 'ROUND_UP' is not definedAnda perlu mengimpor fungsi pembulatan Anda: from decimal import Decimal, ROUND_UP. Fungsi pembulatan lainnya
Stephen Blair
Contoh Anda tampaknya masih berbahaya: Anda mengandalkan pembulatan yang diberikan oleh str ().
YvesgereY
21

round(5.59, 1)bekerja dengan baik. Masalahnya adalah 5.6 tidak dapat direpresentasikan dengan tepat dalam binary floating point.

>>> 5.6
5.5999999999999996
>>> 

Seperti yang dikatakan Vinko, Anda dapat menggunakan pemformatan string untuk melakukan pembulatan tampilan.

Python memiliki modul untuk aritmatika desimal jika Anda membutuhkannya.

Will Harris
sumber
1
Ini bukan lagi masalah dengan Python 2.7 atau Python 3.5
vy32
15

Anda mendapatkan '5.6' jika Anda melakukannya, str(round(n, 1))bukan hanya round(n, 1).

Tomi Kyöstilä
sumber
10

Anda dapat mengganti tipe data menjadi integer:

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

Dan kemudian tampilkan angkanya dengan memasukkan pemisah desimal lokal.

Namun, jawaban Jimmy lebih baik.

Frank Krueger
sumber
5

Matematika floating point rentan terhadap ketidakakuratan presisi yang ringan, tetapi mengganggu. Jika Anda dapat bekerja dengan integer atau titik tetap, Anda akan dijamin presisi.

spoulson
sumber
5

Lihatlah modul Desimal

Desimal "didasarkan pada model floating-point yang dirancang dengan mempertimbangkan orang-orang, dan harus memiliki prinsip panduan terpenting - komputer harus menyediakan aritmatika yang berfungsi dengan cara yang sama seperti aritmatika yang dipelajari orang di sekolah.” - kutipan dari spesifikasi aritmatika desimal.

dan

Angka desimal dapat direpresentasikan dengan tepat. Sebaliknya, angka seperti 1.1 dan 2.2 tidak memiliki representasi yang tepat dalam titik mengambang biner. Pengguna akhir biasanya tidak mengharapkan 1.1 + 2.2 untuk ditampilkan sebagai 3.3000000000000003 seperti halnya dengan titik mengambang biner.

Desimal menyediakan jenis operasi yang memudahkan penulisan aplikasi yang memerlukan operasi floating point dan juga perlu menyajikan hasil tersebut dalam format yang dapat dibaca manusia, misalnya, akuntansi.

Jesse Dhillon
sumber
4

printf si pengisap.

print '%.1f' % 5.59  # returns 5.6
Jason Navarrete
sumber
4

Ini memang masalah besar. Coba kode ini:

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

Ini menampilkan 4,85. Kemudian Anda melakukan:

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

dan itu menunjukkan 4.8. Apakah Anda menghitung dengan tangan, jawaban pastinya adalah 4,85, tetapi jika Anda mencoba:

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

Anda bisa melihat kebenarannya: titik pelampung disimpan sebagai penjumlahan terbatas terdekat dari pecahan yang penyebutnya adalah pangkat dua.

Alexandre Lymberopoulos
sumber
3

Anda dapat menggunakan operator format string %, mirip dengan sprintf.

mystring = "%.2f" % 5.5999
nlucaroni.dll
sumber
2

Bekerja dengan Sempurna

format(5.59, '.1f') # to display
float(format(5.59, '.1f')) #to round
Станислав Повышев
sumber
2

Saya lakukan:

int(round( x , 0))

Dalam hal ini, pertama kita membulatkan dengan benar pada level unit, kemudian kita ubah menjadi integer untuk menghindari pencetakan float.

begitu

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

Saya pikir jawaban ini bekerja lebih baik daripada memformat string, dan juga lebih masuk akal bagi saya untuk menggunakan fungsi bulat.

Gildas
sumber
2

Saya akan menghindari mengandalkan round()sama sekali dalam kasus ini. Mempertimbangkan

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

akan mengeluarkan

61.3
1.29

yang bukan merupakan keluaran yang diinginkan jika Anda membutuhkan pembulatan solid ke bilangan bulat terdekat. Untuk melewati perilaku ini, ikuti math.ceil()(atau math.floor()jika Anda ingin membulatkan ke bawah):

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)

keluaran

61.3
1.3

Semoga membantu.

Tali Oat
sumber
1

Kode:

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'

Keluaran:

5.6
5.7
Dondon Jie
sumber
0

Di sinilah saya melihat ronde gagal. Bagaimana jika Anda ingin membulatkan 2 angka ini menjadi satu tempat desimal? 23.45 23.55 Pendidikan saya adalah dari pembulatan ini anda akan mendapatkan: 23.4 23.6 "aturannya" adalah anda harus membulatkan jika bilangan sebelumnya ganjil, bukan pembulatan jika bilangan sebelumnya genap. Fungsi bulat di python hanya memotong 5.

Gregory Pittman
sumber
1
Yang Anda bicarakan adalah "pembulatan bankir" , salah satu dari banyak cara berbeda untuk melakukan pembulatan.
Simon MᶜKenzie
0

Masalahnya hanya jika digit terakhir adalah 5. Mis. 0,045 disimpan secara internal sebagai 0,044999999999999 ... Anda cukup menaikkan digit terakhir menjadi 6 dan membulatkannya. Ini akan memberi Anda hasil yang diinginkan.

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
Syed Is Saqlain
sumber
0

Opsi potensial lainnya adalah:

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)
conmak
sumber
-4

Bagaimana dengan:

round(n,1)+epsilon
ima
sumber
Itu hanya akan berhasil jika pembulatan secara konsisten off dari nomor putaran oleh epsilon. Jika epsilon = .000001kemudian round(1.0/5.0, 1) + epsilonakan mengambil representasi yang tepat 0,2 dan menjadikannya 0,00001. Masalah yang sama buruknya akan terjadi jika epsilon berada di dalam fungsi putaran.
Michael Scott Cuthbert