round () dường như không được làm tròn đúng


123

Tài liệu cho hàm round () nói rằng bạn truyền cho nó một số và các vị trí vượt qua số thập phân để làm tròn. Vì vậy, nó nên làm điều này:

n = 5.59
round(n, 1) # 5.6

Nhưng, trong thực tế, điểm kỳ lạ nổi cũ tốt và bạn nhận được:

5.5999999999999996

Đối với mục đích của UI, tôi cần hiển thị 5.6. Tôi tìm kiếm trên Internet và tìm thấy một số tài liệu cho rằng điều này phụ thuộc vào việc tôi thực hiện Python. Thật không may, điều này xảy ra trên cả máy phát triển Windows của tôi và từng máy chủ Linux mà tôi đã thử. Xem thêm ở đây .

Không tạo ra thư viện tròn của riêng tôi, có cách nào khác không?


4
Tôi đã thử điều này với python 2.7.11 round (5.59) và nó cho kết quả là 5.6 trong cả windows và linux x86 64 bit, Cython? (Liên kết tài liệu được đề cập đã thay đổi ngay bây giờ tôi đoán)
Alex Punnen

2
Trường hợp nó thực sự không hoạt động chính xác là round(5.55, 1) = 5.5.
Dmitry

Câu trả lời:


101

Tôi không thể giúp cách lưu trữ, nhưng ít nhất định dạng hoạt động chính xác:

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

11
tôi đã cố gắng print '%.2f' % 655.665nhưng nó trở lại 655.66, nó phải thế655.67
Liza

1
@Kyrie xem stackoverflow.com/questions/9301690/ . Không chính xác điểm nổi là đáng trách ở đây - "5,665 -> 5,67" nhưng "15,665 -> 15,66". Sử dụng số thập phân nếu bạn cần độ chính xác chính xác.
Jimmy

7
việc này đang hoạt động sau khi tìm kiếm :) from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN# sử dụng trong làm tròn số nổi Decimal(str(655.665)).quantize(Decimal('1.11'), rounding=ROUND_HALF_UP)# Vấn đề và hạn chế ở các điểm nổi
Liza

102

Định dạng hoạt động chính xác ngay cả khi không phải làm tròn:

"%.1f" % n

18
Theo các tài liệu , phong cách định dạng chuỗi này cuối cùng sẽ biến mất. Định dạng kiểu mới sẽ là"{:.1f}".format(n)
theo đó,

2
Không làm tròn chính xác: '%.5f' % 0.988625cho0.98862
schlamar 14/07/2015

@schlamar: Đó cũng là hành vi của round (): round (0.988625,5) cũng cho 0.98862. vòng (0,988626,5) cũng như "% .5f"% 0,988626 cho 0,98863
Vinko Vrsalovic

thật không may "% .2f"% 2.675 sẽ trả về 2.67 - đây có thể là một câu trả lời bất ngờ cho những người sử dụng phương pháp này và mong đợi 2,68
Dion

30

Nếu bạn sử dụng mô-đun thập phân, bạn có thể ước chừng mà không cần sử dụng chức năng 'vòng'. Đây là những gì tôi đã sử dụng để làm tròn đặc biệt là khi viết các ứng dụng tiền tệ:

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

Điều này sẽ trả về một số thập phân là 16,20.


4
Đây là câu trả lời chính tắc - dù sao thì độ chính xác cũng là vấn đề khá nhiều ở mọi nơi. Chắc chắn: đó là một chút dài dòng . Nhưng ném cái mút đó vào một chức năng của người trợ giúp và bạn tốt để định dạng và đi.
Cecil Curry

2
rounding='ROUND_UP'
LMc

Nếu bạn gặp lỗi này, NameError: global name 'ROUND_UP' is not definedbạn cần nhập chức năng làm tròn của mình : from decimal import Decimal, ROUND_UP. Các chức năng làm tròn khác
Stephen Blair

Ví dụ của bạn có vẻ vẫn nguy hiểm: bạn dựa vào làm tròn được cung cấp bởi str ().
YvesgereY

21

round(5.59, 1)đang hoạt động tốt Vấn đề là 5.6 không thể được biểu diễn chính xác trong dấu phẩy động nhị phân.

>>> 5.6
5.5999999999999996
>>> 

Như Vinko nói, bạn có thể sử dụng định dạng chuỗi để làm tròn để hiển thị.

Python có một mô-đun cho số học thập phân nếu bạn cần điều đó.


1
Đây không còn là vấn đề với Python 2.7 hoặc Python 3.5
vy32

15

Bạn nhận được '5,6' nếu bạn làm str(round(n, 1))thay vì chỉ round(n, 1).


10

Bạn có thể chuyển loại dữ liệu sang một số nguyên:

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

Và sau đó hiển thị số bằng cách chèn dấu phân cách thập phân của miền địa phương.

Tuy nhiên, câu trả lời của Jimmy là tốt hơn.


5

Toán học dấu phẩy động dễ bị tổn thương bởi sự thiếu chính xác, nhưng khó chịu, chính xác. Nếu bạn có thể làm việc với số nguyên hoặc điểm cố định, bạn sẽ được đảm bảo độ chính xác.


5

Hãy xem mô-đun thập phân

Decimal cảm ứng dựa trên mô hình dấu phẩy động được thiết kế dành cho mọi người và nhất thiết phải có nguyên tắc hướng dẫn tối quan trọng - máy tính phải cung cấp số học hoạt động giống như số học mà mọi người học ở trường. - trích từ đặc tả số học thập phân.

Số thập phân có thể được biểu diễn chính xác. Ngược lại, các số như 1.1 và 2.2 không có biểu diễn chính xác trong dấu phẩy động nhị phân. Người dùng cuối thường không mong đợi 1.1 + 2.2 hiển thị là 3.3000000000000003 như với điểm nổi nhị phân.

Decimal cung cấp loại hoạt động giúp dễ dàng viết các ứng dụng yêu cầu hoạt động dấu phẩy động và cũng cần trình bày các kết quả đó theo định dạng có thể đọc được của con người, ví dụ: kế toán.



4

Đó thực sự là một vấn đề lớn. Hãy thử mã này:

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

Nó hiển thị 4,85. Sau đó, bạn làm:

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

và nó hiển thị 4.8. Bạn có tính toán bằng tay câu trả lời chính xác là 4,85 không, nhưng nếu bạn thử:

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

bạn có thể thấy sự thật: điểm nổi được lưu trữ dưới dạng tổng phân số hữu hạn gần nhất có mẫu số là lũy thừa của hai.


3

Bạn có thể sử dụng toán tử định dạng chuỗi %, tương tự như sprintf.

mystring = "%.2f" % 5.5999

2

Hoạt động hoàn hảo

format(5.59, '.1f') # to display
float(format(5.59, '.1f')) #to round

2

Tôi đang làm:

int(round( x , 0))

Trong trường hợp này, trước tiên chúng ta làm tròn đúng ở cấp đơn vị, sau đó chúng tôi chuyển đổi thành số nguyên để tránh in nổi.

vì thế

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

Tôi nghĩ rằng câu trả lời này hoạt động tốt hơn so với việc tạo chuỗi, và nó cũng có ý nghĩa hơn đối với tôi khi sử dụng hàm tròn.


2

Tôi sẽ tránh dựa vào round()tất cả trong trường hợp này. Xem xét

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

sẽ xuất

61.3
1.29

không phải là đầu ra mong muốn nếu bạn cần làm tròn số nguyên đến số nguyên gần nhất. Để bỏ qua hành vi này, hãy đi với math.ceil()(hoặc math.floor()nếu bạn muốn làm tròn xuống):

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)

đầu ra

61.3
1.3

Mong rằng sẽ giúp.


1

Mã số:

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'

Đầu ra:

5.6
5.7

0

Đây là nơi tôi thấy vòng thất bại. Điều gì nếu bạn muốn làm tròn 2 số này đến một chữ số thập phân? 23,45 23,55 Giáo dục của tôi là từ làm tròn số này bạn sẽ nhận được: 23,4 23,6 "quy tắc" là bạn nên làm tròn nếu số trước là số lẻ, không làm tròn nếu số trước là số chẵn. Hàm tròn trong python chỉ đơn giản là cắt 5.


1
Điều bạn đang nói đến là "làm tròn ngân hàng" , một trong nhiều cách khác nhau để thực hiện làm tròn số.
Simon MᶜKenzie

0

Vấn đề chỉ là khi chữ số cuối cùng là 5. Ví dụ. 0,045 được lưu trữ nội bộ là 0,044999999999999 ... Bạn có thể chỉ cần tăng chữ số cuối cùng lên 6 và làm tròn số. Điều này sẽ cung cấp cho bạn kết quả mong muốn.

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

Một lựa chọn tiềm năng khác là:

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

Thế còn:

round(n,1)+epsilon

Điều đó sẽ chỉ hoạt động nếu làm tròn liên tục từ số tròn bằng epsilon. Nếu epsilon = .000001sau đó round(1.0/5.0, 1) + epsilonsẽ lấy đại diện chính xác 0,2 và làm cho nó 0,00001. Các vấn đề tồi tệ tương tự sẽ xảy ra nếu epsilon nằm trong hàm tròn.
Michael Scott Cuthbert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.