round () ดูเหมือนจะปัดเศษไม่ถูกต้อง


123

เอกสารประกอบสำหรับฟังก์ชันround ()ระบุว่าคุณส่งตัวเลขและตำแหน่งที่ผ่านจุดทศนิยมไปจนถึงรอบ ดังนั้นควรทำสิ่งนี้:

n = 5.59
round(n, 1) # 5.6

แต่ในความเป็นจริงแล้วความแปลกประหลาดของจุดลอยตัวเก่า ๆ กำลังคืบคลานเข้ามาและคุณจะได้รับ:

5.5999999999999996

สำหรับวัตถุประสงค์ของ UI 5.6ที่ฉันต้องแสดงผล ฉันโผล่ไปทั่วอินเทอร์เน็ตและพบเอกสารบางอย่างที่ขึ้นอยู่กับการใช้งาน Python ของฉัน น่าเสียดายที่สิ่งนี้เกิดขึ้นกับทั้งเครื่อง Windows dev และเซิร์ฟเวอร์ Linux แต่ละเครื่องที่ฉันได้ลองใช้ ดูที่นี่ด้วย

ขาดการสร้างห้องสมุดกลมของตัวเองมีวิธีใดบ้าง?


4
ฉันลองใช้ python 2.7.11 round (5.59) และให้ผลลัพธ์เป็น 5.6 ทั้งใน windows และ linux x86 64 bit machine, Cython? (ลิงค์เอกสารที่กล่าวถึงมีการเปลี่ยนแปลงตอนนี้ฉันเดา)
Alex Punnen

2
round(5.55, 1) = 5.5อยู่ที่ไหนก็จริงทำงานไม่ถูกต้องคือ
Dmitry

คำตอบ:


102

ฉันไม่สามารถช่วยวิธีจัดเก็บได้ แต่อย่างน้อยการจัดรูปแบบก็ทำงานได้อย่างถูกต้อง:

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

11
ฉันพยายามprint '%.2f' % 655.665แต่มันกลับ655.66มาควรจะเป็น655.67
Liza

1
@ Kyrie ดูที่stackoverflow.com/questions/9301690/… . ความไม่ถูกต้องของจุดลอยตัวให้โทษที่นี่ - "5.665 -> 5.67" แต่ "15.665 -> 15.66" ใช้ทศนิยมหากคุณต้องการความแม่นยำที่แน่นอน
Jimmy

7
ใช้งานได้หลังจากค้นหา :) from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN# ใช้ในการปัดเศษตัวเลขลอยDecimal(str(655.665)).quantize(Decimal('1.11'), rounding=ROUND_HALF_UP)# ปัญหาและข้อ จำกัด ในจุดลอย
Liza

102

การจัดรูปแบบทำงานได้อย่างถูกต้องแม้ไม่ต้องปัดเศษ:

"%.1f" % n

18
ตามเอกสารการจัดรูปแบบสตริงลักษณะนี้จะหายไปในที่สุด รูปแบบใหม่จะเป็น"{:.1f}".format(n)
whereswalden

2
ปัดไม่ถูกต้อง: '%.5f' % 0.988625ให้0.98862
schlamar

@schlamar: นั่นคือพฤติกรรมของ round () เช่นกัน: round (0.988625,5) ยังให้ 0.98862 รอบ (0.988626,5) เช่นเดียวกับ "% .5f"% 0.988626 ให้ 0.98863
Vinko Vrsalovic

น่าเสียดายที่ "% .2f"% 2.675 จะกลับมา 2.67 - ซึ่งอาจเป็นคำตอบที่ไม่คาดคิดสำหรับผู้ที่ใช้วิธีนี้และคาดว่าจะได้ 2.68
Dion

30

หากคุณใช้โมดูลทศนิยมคุณสามารถประมาณได้โดยไม่ต้องใช้ฟังก์ชัน 'รอบ' นี่คือสิ่งที่ฉันใช้ในการปัดเศษโดยเฉพาะเมื่อเขียนแอปพลิเคชันทางการเงิน:

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

สิ่งนี้จะส่งคืนเลขฐานสิบซึ่งเป็น 16.20


4
นี่คือคำตอบที่เป็นที่ยอมรับ - ซึ่งความถูกต้องมีความสำคัญอยู่แล้วซึ่งก็มีอยู่ทั่วไป แน่ใจว่า: มันเป็นบิต verbose แต่โยนเครื่องดูดนั้นในฟังก์ชั่นตัวช่วยและคุณก็สามารถจัดรูปแบบและดำเนินการได้
Cecil Curry

2
rounding='ROUND_UP'
LMc

ถ้าคุณได้รับข้อผิดพลาดนี้คุณจำเป็นต้องนำเข้าฟังก์ชั่นการปัดเศษของคุณ:NameError: global name 'ROUND_UP' is not defined ฟังก์ชันการปัดเศษอื่น ๆfrom decimal import Decimal, ROUND_UP
Stephen Blair

ตัวอย่างของคุณดูเหมือนจะยังอันตรายอยู่: คุณต้องอาศัยการปัดเศษที่ str () ให้ไว้
YvesgereY

21

round(5.59, 1)ทำงานได้ดี ปัญหาคือ 5.6 ไม่สามารถแสดงอย่างแน่นอนในจุดลอยตัวไบนารี

>>> 5.6
5.5999999999999996
>>> 

ตามที่ Vinko กล่าวคุณสามารถใช้การจัดรูปแบบสตริงเพื่อปัดเศษสำหรับการแสดงผล

Python มีโมดูลสำหรับเลขคณิตทศนิยมหากคุณต้องการ


1
นี่ไม่ใช่ปัญหาอีกต่อไปกับ Python 2.7 หรือ Python 3.5
vy32



5

คณิตศาสตร์ทศนิยมมีความเสี่ยงที่จะเกิดความไม่ถูกต้องแม่นยำเล็กน้อย แต่น่ารำคาญ หากคุณสามารถทำงานกับจำนวนเต็มหรือจุดคงที่คุณจะรับประกันความแม่นยำ


5

ลองดูที่โมดูลทศนิยม

ทศนิยม“ ขึ้นอยู่กับแบบจำลองจุดลอยตัวซึ่งได้รับการออกแบบโดยคำนึงถึงผู้คนและจำเป็นต้องมีหลักการชี้นำที่สำคัญยิ่ง - คอมพิวเตอร์ต้องจัดเตรียมเลขคณิตที่ทำงานในลักษณะเดียวกับเลขคณิตที่ผู้คนเรียนรู้ที่โรงเรียน” - ตัดตอนมาจากข้อกำหนดทางคณิตศาสตร์ทศนิยม

และ

ตัวเลขทศนิยมสามารถแสดงได้อย่างแน่นอน ในทางตรงกันข้ามตัวเลขเช่น 1.1 และ 2.2 ไม่มีการแทนค่าที่แน่นอนในจุดลอยตัวไบนารี โดยทั่วไปแล้วผู้ใช้ทั่วไปจะไม่คาดหวังให้ 1.1 + 2.2 แสดงเป็น 3.3000000000000003 เหมือนกับจุดลอยตัวไบนารี

ทศนิยมเป็นรูปแบบการดำเนินการที่ทำให้ง่ายต่อการเขียนแอพที่ต้องใช้การดำเนินการแบบทศนิยมและยังต้องนำเสนอผลลัพธ์เหล่านั้นในรูปแบบที่มนุษย์อ่านได้เช่นการบัญชี



4

เป็นปัญหาใหญ่แน่นอน ลองใช้รหัสนี้:

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

จะแสดง 4.85 จากนั้นคุณจะทำ:

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

และแสดง 4.8 คุณคำนวณด้วยมือไหมคำตอบที่แน่นอนคือ 4.85 แต่ถ้าคุณลอง:

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

คุณสามารถเห็นความจริง: จุดลอยตัวจะถูกจัดเก็บเป็นผลรวม จำกัด ที่ใกล้ที่สุดของเศษส่วนซึ่งตัวส่วนเป็นพลังของสอง


3

คุณสามารถใช้ตัวดำเนินการรูปแบบสตริง%คล้ายกับ sprintf

mystring = "%.2f" % 5.5999


2

ฉันกำลังทำ:

int(round( x , 0))

ในกรณีนี้เราจะปัดเศษอย่างถูกต้องที่ระดับหน่วยก่อนจากนั้นเราจะแปลงเป็นจำนวนเต็มเพื่อหลีกเลี่ยงการพิมพ์ทศนิยม

ดังนั้น

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

ฉันคิดว่าคำตอบนี้ได้ผลดีกว่าการจัดรูปแบบสตริงและยังทำให้ฉันรู้สึกดีขึ้นในการใช้ฟังก์ชัน round


2

ฉันจะหลีกเลี่ยงการพึ่งพาround()เลยในกรณีนี้ พิจารณา

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

จะส่งออก

61.3
1.29

ซึ่งไม่ใช่ผลลัพธ์ที่ต้องการหากคุณต้องการการปัดเศษเป็นจำนวนเต็มที่ใกล้ที่สุด หากต้องการหลีกเลี่ยงพฤติกรรมนี้ให้ไปที่math.ceil()(หรือmath.floor()หากคุณต้องการปัดเศษ):

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)

เอาท์พุท

61.3
1.3

หวังว่าจะช่วยได้



0

ที่นี่ฉันเห็นรอบล้มเหลว จะเกิดอะไรขึ้นถ้าคุณต้องการปัดเศษตัวเลข 2 ตัวนี้เป็นทศนิยมหนึ่งตำแหน่ง? 23.45 23.55 การศึกษาของฉันคือจากการปัดเศษสิ่งเหล่านี้คุณควรจะได้รับ: 23.4 23.6 "กฎ" ที่คุณควรจะปัดเศษขึ้นหากตัวเลขก่อนหน้าเป็นเลขคี่อย่าปัดเศษขึ้นหากเลขนำหน้าเป็นเลขคู่ ฟังก์ชัน round ใน python จะตัดทอน 5


1
สิ่งที่คุณกำลังพูดถึงคือ"การปัดเศษของเจ้ามือ"ซึ่งเป็นหนึ่งในวิธีต่างๆมากมายในการปัดเศษ
Simon MᶜKenzie

0

ปัญหาคือเมื่อหลักสุดท้ายคือ 5 เท่านั้นเช่น 0.045 ถูกเก็บไว้ภายในเป็น 0.044999999999999 ... คุณสามารถเพิ่มตัวเลขสุดท้ายเป็น 6 และปัดเศษ สิ่งนี้จะให้ผลลัพธ์ที่ต้องการ

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

อีกทางเลือกหนึ่งที่เป็นไปได้คือ:

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

สิ่งที่เกี่ยวกับ:

round(n,1)+epsilon

นั่นจะใช้ได้ผลก็ต่อเมื่อการปัดเศษออกจากจำนวนรอบโดย epsilon อย่างสม่ำเสมอ ถ้าเป็นเช่นepsilon = .000001นั้นround(1.0/5.0, 1) + epsilonจะใช้การแทนค่าที่แม่นยำ 0.2 และทำให้เป็น 0.00001 ปัญหาที่ไม่ดีพอ ๆ กันจะเกิดขึ้นหาก epsilon อยู่ในฟังก์ชัน round
Michael Scott Cuthbert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.