python - الحد يطفو إلى نقطتين عشريتين




floating-point rounding (13)

tldr.)

تم حل مشكلة التقريب عند الإدخال / الإخراج بواسطة Python 2.7.0 و 3.1 بشكل قاطع.

اختبار لانهائي:

import random
for x in iter(random.random, None):           # verify FOREVER that rounding is fixed :-)
    assert float(repr(x)) == x                # Reversible repr() conversion
    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 the supermaket with Python 2.7+ :-)

مستندات

انظر ملاحظات الإصدار بيثون 2.7 - لغة أخرى يغير الفقرة الرابعة:

يتم الآن تقريب التحويلات بين الأرقام والفواصل العائمة على معظم الأنظمة الأساسية. تحدث هذه التحويلات في العديد من الأماكن المختلفة: str () على العوامات والأرقام المركبة ؛ الصانعين تعويم ومعقدة. تنسيق رقمي ؛ التسلسل والعوائق التسلسلية والأرقام المعقدة باستخدام وحدات المارشال والمخلل وجونس. تحليل التعويم والحروف التخيلية في كود بيثون ؛ وتحويل عشري إلى تعويم.

فيما يتعلق بهذا ، فإن repr () من رقم النقطة العائمة x يعيد الآن نتيجة استنادًا إلى أقصر سلسلة عشرية يضمن إعادة تدويرها إلى x تحت التقريب الصحيح (مع وضع التقريب نصف دائرية). في السابق كان يعطي سلسلة مبنية على تقريب x إلى 17 رقم عشري.

القضية ذات الصلة

EDIT - مزيد من المعلومات:: كان تنسيق float قبل Python 2.7 مماثلًا numpy.float64 الحالي. يستخدم كلا النوعين نفس الدقة المزدوجة 64 بت IEEE 754 مع mantisa 52 بت. فرق كبير هو أن np.float64.__repr__ يتم تنسيقه بشكل متكرر مع رقم عشري مفرط بحيث لا يمكن فقد أي بت ، ولكن لا يوجد رقم IEEE 754 صالح بين 13.949999999999999 و 13.950000000000001 ، والنتيجة ليست لطيفة و repr(float(number_as_string)) التحويل repr(float(number_as_string)) غير قابل للعكس. على الجانب الآخر: يتم تنسيق float.__repr__ بحيث يكون كل رقم مهمًا ، ويكون التسلسل بدون فجوات ويكون التحويل قابلاً للعكس. ببساطة: إذا كان لديك رقم numpy.float64 ، قم بتحويله إلى عوامة عادية لكي يتم تنسيقه للبشر ليس للمعالجات الرقمية ، وإلا ليس هناك ما هو أكثر ضروري مع Python 2.7+.

أريد أن يتم تقريبه إلى 13.95 .

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

الدالة round لا تعمل بالطريقة التي كنت أتوقعها.


Python 2.7 ،

آمل أن هذا سوف يساعد

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

استعمال

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

بدلا من

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

لأن هذا الأخير قد يؤدي إلى خطأ في الإخراج عند محاولة إخراج متغيرات متعددة (انظر التعليقات)


الطريقة التي أستخدمها هي طريقة تشريح السلسلة. انها سريعة نسبيا وبسيطة.

أولاً ، قم بتحويل العوامة إلى سلسلة ، واختر الطول الذي تريده أن يكون.

float = str(float)[:5]

في السطر الأول أعلاه ، قمنا بتحويل القيمة إلى سلسلة ، ثم أبقت السلسلة فقط على أول أربعة أحرف أو أحرف (شاملة).

امل ان يساعد!


جرب الرموز أدناه:

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

كما أشارMatt ، يوفر Python 3.6 سلاسل f ، ويمكنه أيضًا استخدام المعلمات المتداخلة :

value = 2.34558
precision = 2
width = 4

print(f'result: {value:{width}.{precision}f}')

الذي سيعرض result: 2.35


لإصلاح النقطة العائمة في نوع اللغات الحيوية مثل 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

يمكنك أيضًا استخدام Decimal مثل follwoing:

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

لا يبدو أن أحدًا قد ذكر ذلك حتى الآن ، لذا دعوني أعطي مثالاً في تنسيق السلسلة f / string-string لـ Python 3.6 ، والذي أعتقد أنه أنيق بشكل جميل:

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

إنه يعمل بشكل جيد مع أمثلة أطول أيضًا ، مع المشغلين وليسوا بحاجة إلى parens:

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

لتقريب رقم إلى دقة ، فإن أفضل طريقة هي الطريقة التالية ، والتي يمكن أن تعمل مع أي قرار (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

ما يمكنك القيام به هو تعديل تنسيق الإخراج:

>>> a = 13.95
>>> a
13.949999999999999
>>> print "%.2f" % a
13.95

هناك مواصفات تنسيق جديد ، مواصفات تنسيق سلسلة مصغرة اللغة :

يمكنك القيام بنفس الشيء:

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

لاحظ أن أعلاه إرجاع سلسلة. من أجل الحصول على تعويم ، ببساطة التفاف مع float(...)

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

لاحظ أن التفاف مع float() لا يغير أي شيء:

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

يحتوي البرنامج التعليمي python على ملحق يسمى: Floating Point Arithmetic: Issues and Limitations . اقرأها. وهذا ما يفسر ما يحدث ولماذا يفعل بيثون قصارى جهده. لديها حتى على سبيل المثال الذي يطابق لك. دعني اقتبس قليلا:

>>> 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 .


orig_float = 232569 / 16000.0

14.5355625

short_float = float("{:.2f}".format(orig_float)) 

14.54





precision