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