การ จำกัด การลอยให้เป็นทศนิยมสองตำแหน่ง


1691

ฉันต้องการaที่จะปัดเศษ13.95

>>> a
13.949999999999999
>>> round(a, 2)
13.949999999999999

roundฟังก์ชั่นไม่ได้ทำงานในแบบที่ผมคาดไว้



6
อืม ... คุณพยายามเป็นตัวแทนของสกุลเงินเหรอ? ถ้าเป็นเช่นนั้นคุณไม่ควรใช้เงินเป็นดอลลาร์ คุณอาจใช้ลอยสำหรับเพนนีหรือหน่วยสกุลเงินที่เล็กที่สุดที่คุณพยายามสร้างแบบจำลอง แต่วิธีปฏิบัติที่ดีที่สุดคือการใช้การแทนทศนิยมตามที่ HUAGHAGUAH แนะนำในคำตอบของเขา
— SingleNegationElimination

63
มันเป็นสิ่งสำคัญที่จะไม่แสดงสกุลเงินในการลอย ลอยไม่แม่นยำ แต่จำนวนเงินเพนนีหรือเซ็นต์เป็นจำนวนเต็ม ดังนั้นจำนวนเต็มเป็นวิธีที่ถูกต้องในการเป็นตัวแทนของสกุลเงิน
— Davoud Taghawi-Nejad

2
@ DavoudTaghawi-Nejad หรือมากกว่านั้น ... The Decimal Type
— พื้นฐาน

17
ฉันอาจมาสายเกินไปที่นี่ แต่ฉันอยากถามนักพัฒนาของ Python แก้ปัญหานี้ไหม เพราะเมื่อฉันปัดเศษ (13.949999999999999, 2) ฉันก็จะได้ 13.95 ฉันได้ลองใน Python 2.7.6 และ 3.4 แล้ว มันได้ผล. ไม่แน่ใจว่า 2.7 เคยมีในปี 2009 บางทีมันเป็น Python 2.5 หรือเปล่า
— bad_keypoints

คำตอบ:


1690

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

ด้วยการเป็นตัวแทนจุดลอยรุ่นกลมของคุณเป็นจำนวนเดียวกัน เนื่องจากคอมพิวเตอร์เป็นเลขฐานสองพวกเขาจะเก็บตัวเลขทศนิยมเป็นจำนวนเต็มแล้วหารด้วยกำลังสองดังนั้น 13.95 จะแสดงในรูปแบบที่คล้ายกันกับ 125650429603636838 / (2 ** 53)

ตัวเลขความแม่นยำสองเท่ามีความแม่นยำ 53 บิต (16 หลัก) และลอยปกติมีความแม่นยำ 24 บิต (8 หลัก) ชนิดลอยจุดในหลามใช้ความแม่นยำสองในการจัดเก็บค่า

ตัวอย่างเช่น,

>>> 125650429603636838/(2**53)
13.949999999999999

>>> 234042163/(2**24)
13.949999988079071

>>> a = 13.946
>>> print(a)
13.946
>>> print("%.2f" % a)
13.95
>>> round(a,2)
13.949999999999999
>>> print("%.2f" % round(a, 2))
13.95
>>> print("{:.2f}".format(a))
13.95
>>> print("{:.2f}".format(round(a, 2)))
13.95
>>> print("{:.15f}".format(round(a, 2)))
13.949999999999999

หากคุณอยู่หลังตำแหน่งทศนิยมเพียงสองตำแหน่งเท่านั้น (เพื่อแสดงค่าสกุลเงิน) คุณจะมีตัวเลือกที่ดีกว่า:

  1. ใช้จำนวนเต็มและเก็บค่าเป็นเซ็นต์ไม่ใช่ดอลลาร์แล้วหารด้วย 100 เพื่อแปลงเป็นดอลลาร์
  2. หรือใช้เป็นจำนวนจุดคงเหมือนทศนิยม

27
@Christian มีความแตกต่างพื้นฐานระหว่างค่าที่จัดเก็บและวิธีที่คุณแสดงค่านั้น การจัดรูปแบบผลลัพธ์ควรอนุญาตให้คุณเพิ่มการเติมได้ตามต้องการรวมถึงการเพิ่มตัวคั่นด้วยเครื่องหมายจุลภาค ฯลฯ
— พื้นฐาน

22
มูลค่าการกล่าวขวัญว่า"%.2f" % round(a,2)คุณสามารถใส่ไม่เพียง แต่ใน printf แต่ยังในสิ่งต่าง ๆ เช่นstr()
— andilabs

20
ทำไมผู้คนมักคิดว่าสกุลเงินในการปัดเศษทศนิยม บางครั้งคุณแค่ต้องการทำงานด้วยความแม่นยำน้อยลง
— worc

9
@radtek: คุณต้องเข้าใจว่าค่าไบนารี (ชนิดfloat) เป็นเพียงการประมาณที่ใกล้ที่สุดของเลขทศนิยม (ที่คุณคุ้นเคยกับมนุษย์) ไม่มีค่าเลขฐานสองดังกล่าว (สามารถแสดงได้อย่างละเอียด) เป็น 0.245 มันก็ไม่อยู่และคณิตศาสตร์ไม่สามารถมีชีวิตอยู่ ค่าไบนารีที่ใกล้เคียงกับ 0.245 น้อยกว่า 0.245 เล็กน้อยดังนั้นโดยทั่วไปแล้วจะปัดเศษลง ในทำนองเดียวกันไม่มีสิ่งเช่น 0.225 ในไบนารี แต่ค่าไบนารีซึ่งใกล้เคียงกับ 0.225 มากกว่า 0.225 เล็กน้อยดังนั้นโดยธรรมชาติมันจะปัดขึ้น
— John Y

12
@radtek: คุณขอคำอธิบายอย่างแท้จริง ทางออกที่ตรงไปตรงมาที่สุดคือการใช้Decimalและนั่นก็เป็นหนึ่งในโซลูชั่นที่นำเสนอในคำตอบนี้ อีกอันคือการแปลงปริมาณของคุณเป็นจำนวนเต็มและใช้เลขคณิตเลขจำนวนเต็ม ทั้งสองวิธีนี้ยังปรากฏในคำตอบและความคิดเห็นอื่น ๆ
— John Y

586

มีข้อกำหนดรูปแบบใหม่ข้อกำหนดรูปแบบสตริงเป็นภาษามินิ :

คุณสามารถทำเช่นเดียวกับ:

"{:.2f}".format(13.949999999999999)

หมายเหตุ 1:ข้างต้นส่งกลับสตริง หากต้องการลอยตัวให้ห่อด้วยfloat(...):

float("{:.2f}".format(13.949999999999999))

หมายเหตุ 2: การหุ้มด้วยfloat()จะไม่เปลี่ยนแปลงอะไรเลย:

>>> x = 13.949999999999999999
>>> x
13.95
>>> g = float("{:.2f}".format(x))
>>> g
13.95
>>> x == g
True
>>> h = round(x, 2)
>>> h
13.95
>>> x == h
True

17
เพื่อเพิ่มเครื่องหมายจุลภาคเช่นกันคุณสามารถ'{0:,.2f}'.format(1333.949999999)พิมพ์'1,333.95'ได้
— สตีเฟ่นบลัม

@ OnurYıldırım: ใช่ แต่คุณสามารถห่อมันด้วยfloat(); float("{0:.2f}".format(13.9499999))
— Jossef Harush

5
@JossefHarush คุณสามารถห่อด้วย float () แต่คุณยังไม่ได้รับอะไรเลย ตอนนี้คุณมีการลอยอีกครั้งโดยมีความไม่แน่นอนเหมือนกันทั้งหมด 13.9499999999999 และ 13.95 เป็นทศนิยมเดียวกัน
— Ned Batchelder

4
@NedBatchelder: ผมเห็นว่าพวกเขามีค่าเท่ากัน แต่ข้อ จำกัด นี้ลอยไปสองจุดทศนิยม :)
— Jossef Harush

8
อย่างไรก็ตามตั้งแต่ Python 3.6 เราสามารถใช้ f-strings:f"Result is {result:.2f}"
— Andrey Semakin

289

built-in ใช้round()งานได้ดีใน Python 2.7 หรือใหม่กว่า

ตัวอย่าง:

>>> round(14.22222223, 2)
14.22

ตรวจสอบเอกสาร


1
ดังนั้นฉันจะเข้าใจว่านี่เป็น Python 2.7 ล้มเหลวหรือไม่ เหตุใดฟังก์ชันพื้นฐานจึงให้ผลลัพธ์ที่แตกต่างจาก v 2.7 ถึง v 3
— MikeM

แต่round(2.16, 1)ให้2.2เหตุผลว่าทำไมไพ ธ อนจึงเสนอtruncatefunc
— jiamo

ตัวอย่างเช่นหากคุณพยายามปัดค่า 2.675 ไปเป็นทศนิยมสองตำแหน่งคุณจะได้รับ>>> round(2.675, 2) 2.67 docs.python.org/2/tutorial/floatingpoint.html
— danger89

4
จากหน้าเอกสาร Python 3:Note The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float.
— Richard Dally

โปรดทราบว่าหากคุณพยายามใช้วิธีการนี้เพื่อพิมพ์ตัวเลขเช่น 1.00000 มันจะพิมพ์ออกมาเพียง 1.0 ไม่ว่าคุณจะระบุจำนวนทศนิยมเท่าใด
— Josh Correia

142

ฉันรู้สึกว่าวิธีที่ง่ายที่สุดคือการใช้format()ฟังก์ชั่น

ตัวอย่างเช่น:

a = 13.949999999999999
format(a, '.2f')

13.95

สิ่งนี้สร้างจำนวนลอยเป็นสตริงที่ปัดเศษเป็นทศนิยมสองตำแหน่ง


97

ใช้

print"{:.2f}".format(a)

แทน

print"{0:.2f}".format(a)

เพราะหลังอาจนำไปสู่ข้อผิดพลาดการส่งออกเมื่อพยายามที่จะส่งออกหลายตัวแปร (ดูความคิดเห็น)


2
นี่เป็นเรื่องไร้สาระ ประโยคทั้งสองที่กำหนดให้ทำงานเหมือนกันบน Python 2.7 และมีเพียงประโยคที่สองเท่านั้นที่ใช้ได้กับ Python 2.6 (ทั้งคำสั่งไม่ถูกต้องใน Python 3 หรือ Python <2.6.) รูปแบบแรกไม่มีประโยชน์นอกเหนือจากความกะทัดรัด
— Mark Dickinson

1
ฉันหมายถึงพิมพ์ "{0: .2f} {0: .2f}" รูปแบบ (a, b) จะนำไปสู่ข้อผิดพลาดในการส่งออก - มันจะส่งออก 'a' ค่าสองครั้ง ในขณะที่พิมพ์ "{:. 2f} {: .2f}". รูปแบบ (a, b) จะส่งออกค่า 'a' และ 'b'
— Alexey Antonenko

2
สำหรับ Python 3 คุณเพียงแค่ต้องเพิ่มเครื่องหมายวงเล็บ (... ) และภายในพวกเขาทั้งหมดที่ฉันเขียนนั้นถูกต้อง
— Alexey Antonenko

"ฉันหมายถึงพิมพ์" {0: .2f} {0: .2f} ". รูปแบบ (a, b) จะนำไปสู่ข้อผิดพลาดในผลลัพธ์" อา. นั่นเป็นคำสั่งที่แตกต่างออกไป! บางทีคุณควรแก้ไขคำตอบของคุณ? (ยกตัวอย่างเช่น "error error" หมายถึงอะไรในคำตอบปัจจุบันคุณสามารถยกตัวอย่างของกรณีที่ข้อความที่สองยกข้อยกเว้น แต่ข้อแรกไม่ได้)
— Mark Dickinson

3
คุณจะอยู่หลังการพิมพ์ ("{0: .2f} {1: .2f}". รูปแบบ (a, b)) ถ้าคุณมีสองตัวแปร
— Hovo

95

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

>>> "%.2f" % 3.14159
'3.14'
>>> "%.2f" % 13.9499999
'13.95'

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


68

ลองรหัสด้านล่าง:

>>> a = 0.99334
>>> a = int((a * 100) + 0.5) / 100.0 # Adding 0.5 rounds it up
>>> print a
0.99

แต่พึงระวังมูลค่าของ a ยังคงเป็นค่าที่ไม่แน่นอน ดูที่นี่ - repl.it/LJs (คลิก "เรียกใช้เซสชัน" ที่ด้านบนของส่วนด้านขวา)
— ชูชีพ

3
หากคุณใช้วิธีนี้คุณควรเพิ่ม 0.5 เพื่อให้ได้ภาพที่แม่นยำยิ่งขึ้น int (a * 100 + 0.5) / 100.0; การใช้ math.ceil เป็นอีกทางเลือกหนึ่ง
— arhuaco

3
@ShashankSawant: สำหรับสิ่งหนึ่งคำตอบที่นำเสนอไม่ได้กลมมันตัดทอน คำแนะนำในการเพิ่มครึ่งหนึ่งเมื่อสิ้นสุดจะปัดเศษ แต่ก็ไม่มีประโยชน์ที่จะทำเช่นนี้เพียงแค่ใช้roundฟังก์ชันในตอนแรก อีกวิธีหนึ่งเนื่องจากโซลูชันนี้ยังคงใช้จุดลอยตัวปัญหาเดิมของ OP ยังคงอยู่แม้ว่าจะเป็น "การแก้ไข" ของโซลูชัน "นี้"
— John Y

3
-1 นี่เป็นเพียงการนำroundฟังก์ชันมาใช้ใหม่โดยไม่จำเป็น(ซึ่งถูกใช้ในคำถาม)
— ระหว่าง

4
@interjay ซึ่งเป็นสิ่งที่จำเป็นหากใช้round()ไม่ได้ตามที่ OP กล่าวถึง
— Pithikos

57

TLDR;)

ปัญหาการปัดเศษเข้า / ส่งออกได้รับการแก้ไขได้อย่างแน่นอนโดย Python 2.7.0และ3.1

ตัวเลขที่ถูกปัดเศษอย่างถูกต้องสามารถแปลงกลับไปกลับมา
str -> float() -> repr() -> float() ...หรือDecimal -> float -> str -> Decimal
ประเภททศนิยมไม่จำเป็นสำหรับการจัดเก็บอีกต่อไป


(ธรรมชาติก็สามารถมีความจำเป็นที่จะรอบผลมาจากการเพิ่มหรือลบหมายเลขที่โค้งมนเพื่อขจัดข้อผิดพลาดบิตสุดท้ายสะสม. เลขคณิตสิบอย่างชัดเจนสามารถยังคงมีประโยชน์ แต่การแปลงสตริงโดยstr()(นั่นคือมีการปัดเศษถึง 12 ตัวเลขที่ถูกต้อง ) ปกติแล้วจะดีพอหากไม่ต้องการความแม่นยำสูงหรือไม่ต้องใช้การคำนวณทางคณิตศาสตร์จำนวนมาก ๆ )

การทดสอบที่ไม่มีที่สิ้นสุด :

import random
from decimal import Decimal
for x in iter(random.random, None):           # Verify FOREVER that rounding is fixed :-)
    assert float(repr(x)) == x                # Reversible repr() conversion.
    assert float(Decimal(repr(x))) == x
    assert len(repr(round(x, 10))) <= 12      # Smart decimal places in repr() after round.
    if x >= 0.1:                              # Implicit rounding to 12 significant digits
        assert str(x) == repr(round(x, 12))   # by str() is good enough for small errors.
        y = 1000 * x                             # Decimal type is excessive for shopping
        assert str(y) == repr(round(y, 12 - 3))  # in a supermaket with Python 2.7+ :-)

เอกสาร

ดูบันทึกประจำรุ่น Python 2.7 - ภาษาอื่นเปลี่ยนย่อหน้าที่สี่:

การแปลงระหว่างตัวเลขทศนิยมและสตริงนั้นถูกปัดเศษอย่างถูกต้องบนแพลตฟอร์มส่วนใหญ่ การแปลงเหล่านี้เกิดขึ้นในหลาย ๆ แห่ง: str () บนจำนวนลอยและจำนวนเชิงซ้อน ตัวสร้างลอยและซับซ้อน การจัดรูปแบบตัวเลข การทำให้เป็นอนุกรมและการทำให้เป็นอนุกรมลอยและหมายเลขที่ซับซ้อนโดยใช้marshal, pickleและjsonโมดูล; การแยกลอยและตัวอักษรในจินตนาการในรหัสหลาม; และการแปลงทศนิยมเป็นทศนิยม

เกี่ยวข้องกับสิ่งนี้การrepr ()ของตัวเลข floating-x จะส่งคืนผลลัพธ์โดยอิงจากสตริงทศนิยมที่สั้นที่สุดที่รับประกันว่าจะปัดกลับเป็น xภายใต้การปัดเศษที่ถูกต้อง (พร้อมโหมดการปัดเศษ ก่อนหน้านี้มันให้สตริงตามการปัดเศษ x ถึง 17 หลักทศนิยม

ปัญหาที่เกี่ยวข้อง


ข้อมูลเพิ่มเติม:จัดรูปแบบของfloatก่อนหลาม 2.7 numpy.float64ใกล้เคียงกับปัจจุบัน ทั้งสองชนิดใช้ความแม่นยำสองเท่า64 บิตIEEE 754 ที่มี 52 mantissa ความแตกต่างใหญ่คือnp.float64.__repr__มีการจัดรูปแบบบ่อยครั้งด้วยจำนวนทศนิยมที่มากเกินไปเพื่อไม่ให้บิตหายไปได้ แต่ไม่มีหมายเลข IEEE 754 ที่ถูกต้องอยู่ระหว่าง 13.94999999999999999 และ 13.950000000000001 ผลลัพธ์ไม่ดีและการแปลงrepr(float(number_as_string))ไม่สามารถย้อนกลับได้ด้วย numpy ในทางกลับกัน:float.__repr__มีการจัดรูปแบบเพื่อให้ทุกหลักมีความสำคัญ; ลำดับนั้นไม่มีช่องว่างและการแปลงกลับได้ ง่ายๆ: ถ้าคุณอาจมีหมายเลข numpy.float64 ให้แปลงเป็นทศนิยมปกติเพื่อจัดรูปแบบสำหรับมนุษย์ไม่ใช่สำหรับโปรเซสเซอร์ที่เป็นตัวเลขมิฉะนั้นไม่จำเป็นต้องใช้ Python 2.7+ อีกต่อไป


ทำไม downvote คำถามคือเกี่ยวกับ Python float(ความแม่นยำสองเท่า) และปกติroundไม่ใช่เกี่ยวกับ numpy.double และการแปลงเป็นสตริง การปัดเศษ Python ธรรมดาไม่สามารถทำได้ดีกว่าใน Python 2.7 คำตอบส่วนใหญ่เขียนไว้ก่อน 2.7 แต่พวกเขาล้าสมัยแม้ว่าพวกเขาจะดีมากในตอนแรก นี่คือเหตุผลของคำตอบของฉัน
— hynekcer

53 บิตเมื่อคุณรวม "บิตซ่อน" ซึ่งโดยปริยาย1ยกเว้นในระหว่าง "ค่อยเป็นค่อยไปอันเดอร์โฟลว์"
— Rick James

ไม่ใช่ความผิดของกลม แต่เป็นความผิดของหน้าจอ
— Rick James

ใช่มันเป็นที่รู้จักกันดี ฉันคิดถึง แต่บริบทหากคุณคัดค้านบางสิ่งใน Python 2.7 บันทึกย่อประจำรุ่นหรือในข้อความของฉันหรือไม่มีอะไรเลย มันซับซ้อนกว่าความจำเป็นในวัตถุประสงค์ของคำถามนี้ มันควรจะเสริมว่านอกจากนี้ยังมีการแปลงจากสตริงจะลอยได้รับการแก้ไขในหลาม 2.7 เนื่องจากการปัดเศษข้อผิดพลาดใน 32 บิตชิป Intel บางอย่างและว่า "รอบ () ฟังก์ชั่นนี้ยังมีในขณะนี้ที่โค้งมนอย่างถูกต้อง." ( บันทึกประจำรุ่น - คุณสมบัติ 3.1 ย้อนกลับไปที่ 2.7 ) คุณเห็นด้วยไหม
— hynekcer

1
อ๊ะนั่นคือVSa*b b*aขอบคุณสำหรับลิงค์ - Nostalgia
— Rick James

52

ด้วย Python <3 (เช่น 2.6 หรือ 2.7) มีสองวิธีในการทำเช่นนั้น

# Option one 
older_method_string = "%.9f" % numvar

# Option two (note ':' before the '.9f')
newer_method_string = "{:.9f}".format(numvar)

แต่ทราบว่าสำหรับงูหลามรุ่นข้างต้น 3 (เช่น 3.2 หรือ 3.3) ตัวเลือกที่สองคือการแนะนำ

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกที่สองผมขอแนะนำลิงค์นี้ในการจัดรูปแบบสตริงจากเอกสารหลาม

และสำหรับข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกหนึ่งที่ลิงค์นี้จะพอเพียงและมีข้อมูลเกี่ยวกับธงต่างๆ

การอ้างอิง: แปลงหมายเลขทศนิยมเป็นความแม่นยำแน่นอนแล้วคัดลอกไปยังสตริง


คุณเป็นตัวแทนของจำนวนเต็มได้อย่างไร ถ้าฉันใช้ "{i3}". format (numvar) ฉันพบข้อผิดพลาด
— skytux

นี่คือสิ่งที่ผมหมายถึง: ถ้าnumvar=12.456แล้ว"{:.2f}".format(numvar)ผลตอบแทนถัวเฉลี่ย12.46แต่ให้ข้อผิดพลาดและฉันคาดหวัง"{:2i}".format(numvar) 12
— skytux


47

ดูเหมือนจะไม่มีใครพูดถึงเลยดังนั้นขอยกตัวอย่างในรูปแบบ f-string / template-string ของ Python 3.6 ซึ่งฉันคิดว่าเรียบร้อยดี:

>>> f'{a:.2f}'

มันทำงานได้ดีกับตัวอย่างที่ยาวขึ้นด้วยตัวดำเนินการและไม่ต้องการ parens:

>>> print(f'Completed in {time.time() - start:.2f}s')

39

คุณสามารถใช้ตัวดำเนินการรูปแบบสำหรับปัดเศษค่าทศนิยมสูงสุด 2 ตำแหน่งในไพ ธ อน:

print(format(14.4499923, '.2f')) // output is 14.45

4
สตริงส่งคืนนี้
— Jemshit Iskenderov

29

ใน Python 2.7:

a = 13.949999999999999
output = float("%0.2f"%a)
print output

1
มันไม่ได้ช่วยอะไรเลย outputมีเดียวกันแน่นอนคุ้มค่าเป็นaดังนั้นคุณอาจรวมทั้งได้เขียนprint aแทนprint outputในบรรทัดสุดท้าย
— Mark Dickinson

@MarkDickinson คุณช่วยลองอีกครั้งได้ไหม เพราะมันทำงานได้ตามที่คาดไว้ในคอมไพเลอร์ของฉัน
— Shashank Singh

1
คุณพลาดจุดของฉัน ใช่รหัสของคุณพิมพ์13.95ออกมา แต่print aสำหรับค่าเฉพาะนี้aใน Python 2.7 ดังนั้นจึงไม่ชัดเจนว่าจุดของขั้นตอนการจัดรูปแบบคืออะไร
— Mark Dickinson

@ MarkDickinson ฉันได้แก้ไขรหัสแล้ว ฉันยอมรับว่า 'print a' พิมพ์ค่าเดียวกันกับ "print output" แต่ถ้าคุณเปรียบเทียบ "a == เอาต์พุต" ผลลัพธ์จะเป็น "False" เนื่องจากขั้นตอนการจัดรูปแบบจะปัดเศษทศนิยม "a" เป็นทศนิยมสองตำแหน่ง
— Shashank Singh

1
คุณลองa == outputรหัสที่คุณแสดงหรือไม่ มันให้Trueสำหรับฉันและฉันสงสัยว่ามันจะทำเพื่อคุณเช่นกัน
— Mark Dickinson

22

งูหลามกวดวิชามีภาคผนวกที่เรียกว่าFloating Point เลขคณิต: ปัญหาและข้อ จำกัด อ่านมัน มันอธิบายสิ่งที่เกิดขึ้นและทำไม Python จึงพยายามทำให้ดีที่สุด มันยังมีตัวอย่างที่ตรงกับคุณ ให้ฉันพูดเล็กน้อย:

>>> 0.1
0.10000000000000001

คุณอาจถูกล่อลวงให้ใช้round() ฟังก์ชั่นเพื่อตัดกลับเป็นตัวเลขหลักเดียวที่คุณคาดหวัง แต่นั่นก็ไม่ได้ทำให้แตกต่าง:

>>> round(0.1, 1)
0.10000000000000001

ปัญหาคือค่าเลขฐานสองแบบลอยตัวที่เก็บไว้“0.1” นั้นเป็นค่าเลขฐานสองที่ดีที่สุดเท่าที่จะเป็นไปได้1/10ดังนั้นการพยายามปัดมันอีกครั้งไม่สามารถทำให้ดีขึ้นได้: มันดีพอ ๆ กับที่ได้รับ

ผลที่ตามมาอีกประการหนึ่งคือเนื่องจาก0.1 ไม่แน่นอนการ1/10รวมสิบค่าของ0.1อาจไม่ได้ผลอย่างแน่นอน 1.0เช่น:

>>> sum = 0.0
>>> for i in range(10):
...     sum += 0.1
...
>>> sum
0.99999999999999989

ทางเลือกหนึ่งและวิธีแก้ปัญหาของคุณคือการใช้decimalโมดูล




11

ใช้การรวมกันของวัตถุทศนิยมและวิธีการกลม ()

Python 3.7.3
>>> from decimal import Decimal
>>> d1 = Decimal (13.949999999999999) # define a Decimal
>>> d1 
Decimal('13.949999999999999289457264239899814128875732421875')
>>> d2 = round(d1, 2) # round to 2 decimals
>>> d2
Decimal('13.95')

7

สำหรับการแก้ไขจุดลอยตัวในภาษาแบบไดนามิกเช่น Python และ JavaScript ฉันใช้เทคนิคนี้

# For example:
a = 70000
b = 0.14
c = a * b

print c # Prints 980.0000000002
# Try to fix
c = int(c * 10000)/100000
print c # Prints 980

คุณยังสามารถใช้ทศนิยมได้ดังต่อไปนี้:

from decimal import *
getcontext().prec = 6
Decimal(1) / Decimal(7)
# Results in 6 precision -> Decimal('0.142857')

getcontext().prec = 28
Decimal(1) / Decimal(7)
# Results in 28 precision -> Decimal('0.1428571428571428571428571429')

1
getcontext().prec = 6ใช้ได้กับขอบเขตของฟังก์ชั่นหรือสถานที่ทั้งหมดหรือไม่
— Julio Marins

1
บริบทเป็นสภาพแวดล้อมสำหรับการดำเนินการทางคณิตศาสตร์ ควบคุมความแม่นยำตั้งกฎสำหรับการปัดเศษพิจารณาว่าสัญญาณใดที่ถือว่าเป็นข้อยกเว้นและ จำกัด ช่วงสำหรับการยกกำลัง แต่ละเธรดมีบริบทปัจจุบันของตนเอง @JulioMarins
— Siamand

7
from decimal import Decimal


def round_float(v, ndigits=2, rt_str=False):
    d = Decimal(v)
    v_str = ("{0:.%sf}" % ndigits).format(round(d, ndigits))
    if rt_str:
        return v_str
    return Decimal(v_str)

ผล:

Python 3.6.1 (default, Dec 11 2018, 17:41:10)
>>> round_float(3.1415926)
Decimal('3.14')
>>> round_float(3.1445926)
Decimal('3.14')
>>> round_float(3.1455926)
Decimal('3.15')
>>> round_float(3.1455926, rt_str=True)
'3.15'
>>> str(round_float(3.1455926))
'3.15'


5

ฟังก์ชั่นแลมบ์ดาเป็นเช่นนี้:

arred = lambda x,n : x*(10**n)//1/(10**n)

วิธีนี้คุณสามารถทำได้:

arred(3.141591657,2)

และรับ

3.14

4

ง่ายเหมือน 1,2,3:

  1. ใช้โมดูลทศนิยมสำหรับการคำนวณทศนิยมทศนิยมอย่างรวดเร็วอย่างถูกต้อง:

    d = ทศนิยม (10000000.0000009)

เพื่อให้บรรลุการปัดเศษ:

   d.quantize(Decimal('0.01'))

จะได้ผลลัพธ์ด้วย Decimal('10000000.00')

  1. ทำให้เหนือแห้ง:
    def round_decimal(number, exponent='0.01'):
        decimal_value = Decimal(number)
        return decimal_value.quantize(Decimal(exponent))

หรือ

    def round_decimal(number, decimal_places=2):
        decimal_value = Decimal(number)
        return decimal_value.quantize(Decimal(10) ** -decimal_places)
  1. โหวตขึ้นคำตอบนี้ :)

PS: คำติชมของคนอื่น ๆ : การจัดรูปแบบไม่ได้ปัดเศษ


2

วิธีปัดเศษตัวเลขเป็นความละเอียดวิธีที่ดีที่สุดคือสิ่งต่อไปนี้ซึ่งสามารถทำงานได้กับความละเอียดใด ๆ (0.01 สำหรับสองทศนิยมหรือขั้นตอนอื่น ๆ ):

>>> import numpy as np
>>> value = 13.949999999999999
>>> resolution = 0.01
>>> newValue = int(np.round(value/resolution))*resolution
>>> print newValue
13.95

>>> resolution = 0.5
>>> newValue = int(np.round(value/resolution))*resolution
>>> print newValue
14.0

ใช้งานไม่ได้กับฉันใน python 3.4.3 และ numpy 1.9.1 ใช่ไหม >>> นำเข้า numpy as np >>> res = 0.01 >>> value = 0.184 >>> np.round (ค่า / res) * res 0.1799999999999999999
— szeitlin

1
กำลังมองหาเอกสารฉันเห็นว่าปัญหามาจากnumpy.roundความถูกต้อง / ความแม่นยำ ดังนั้นจึงจำเป็นต้องกำหนดว่าเป็น int ก่อนการคูณด้วยความละเอียด ฉันอัปเดตรหัสแล้ว ขอบคุณสำหรับสิ่งนั้น!
— iblasi

ที่จำเป็นเท่านั้นคือการแปลงnumpy.float64ผลมาจากการ np.round หรือเพียงเพื่อการใช้งานfloat round(value, 2)ไม่มีหมายเลข IEEE 754 ที่ถูกต้องอยู่ระหว่าง 13.949999999999999 (= 1395 / 100. ) และ 3.950000000000001 (= 1395 * .01) ทำไมคุณคิดว่าวิธีการของคุณดีที่สุด ค่าดั้งเดิม 13.94999999999999999289 (= value = round (value, 2)) มีความแม่นยำมากกว่าของคุณ 13.95000000000000178 (พิมพ์โดย np.float96) ข้อมูลเพิ่มเติมสำหรับ numpy ตอนนี้เพิ่มลงในคำตอบของฉันที่คุณอาจ downvoted โดยไม่ได้ตั้งใจ เดิมทีมันไม่ได้เกี่ยวกับอะไร
— hynekcer

@ hynekcer ฉันไม่คิดว่าคำตอบของฉันดีที่สุด แค่ต้องการเพิ่มตัวอย่างของ limit float ไปที่ n ทศนิยม แต่ใกล้เคียงที่สุดของความละเอียดที่กำหนด ฉันตรวจสอบตามที่คุณพูดว่าแทนที่จะintใช้floatสำหรับตัวอย่าง @szeitlin ขอบคุณสำหรับความคิดเห็นเพิ่มเติม (ขออภัย แต่ฉันไม่ได้ลงคะแนนให้คุณ)
— iblasi

การเพิ่มการพึ่งพาใหม่ทั้งหมดสำหรับการประมวลผลตัวเลข (pandas) เป็น "วิธีที่ดีที่สุด" หรือไม่
— Hejazzman


-13

วิธีที่ฉันใช้คือการแบ่งสตริง มันค่อนข้างง่ายและรวดเร็ว

ก่อนอื่นให้แปลงทศนิยมเป็นสตริงเลือกความยาวที่คุณต้องการ

float = str(float)[:5]

ในบรรทัดเดียวด้านบนเราได้แปลงค่าเป็นสตริงจากนั้นเก็บสตริงเป็นตัวเลขหรือตัวอักษรสี่ตัวแรกเท่านั้น (รวมอยู่ด้วย)

หวังว่าจะช่วย!


2
โปรดอย่าโพสต์คำตอบที่เหมือนกันสำหรับคำถามหลายข้อ
— vaultah

18
ว้าว ... tdh ... โปรดอย่าทำซอฟต์แวร์บัญชี ... จะเกิดอะไรขึ้นถ้าจำนวนนั้นเป็น 113.94? นี้จะส่งผลใน 113.9 ... ปล่อย 0.04 หายไป .... นอกจากนี้ยังมีคำตอบจากกว่า 5 ปีที่ผ่านมา ....
— Angry 84
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.