تقريب الكسور العشرية والأعداد الصحيحة في بايثون باستخدام “round” و “Decimal.quantize”

اعمال

يوضح ما يلي كيفية تقريب الأرقام في بايثون بالتقريب أو التقريب إلى رقم زوجي. يُفترض أن تكون الأرقام من نوع عائم فاصلة عائمة أو عدد صحيح من النوع int.

  • وظيفة مدمجة (على سبيل المثال في لغة البرمجة):round()
    • تقريب الكسور العشرية إلى أي عدد من الأرقام.
    • تقريب الأعداد الصحيحة إلى أي عدد من الأرقام.
    • round () إلى رقم زوجي ، وليس إلى تقريب مشترك
  • مكتبة قياسيةdecimalquantize()
    • Decimalخلق كائن
    • تقريب الكسور العشرية لأي عدد من الخانات والتقريب لأرقام زوجية
    • تقريب الأعداد الصحيحة إلى أي عدد من الخانات والتقريب لأرقام زوجية
  • حدد وظيفة جديدة
    • تقريب الكسور العشرية إلى أي عدد من الأرقام.
    • تقريب الأعداد الصحيحة إلى أي عدد من الأرقام
    • ملاحظة: للقيم السالبة

لاحظ أنه ، كما هو مذكور أعلاه ، لا تعد جولة الدالة المضمنة تقريبًا عامًا ، ولكنها تقريب إلى رقم زوجي. انظر أدناه للحصول على التفاصيل.

وظيفة مدمجة (على سبيل المثال في لغة البرمجة):round()

يتم توفير Round () كوظيفة مضمنة. يمكن استخدامه دون استيراد أي وحدات.

الوسيطة الأولى هي الرقم الأصلي ، والوسيطة الثانية هي عدد الأرقام (عدد الأرقام التي يجب التقريب إليها).

تقريب الكسور العشرية إلى أي عدد من الأرقام.

فيما يلي مثال على معالجة نوع عائم الفاصلة العائمة.

إذا تم حذف الوسيطة الثانية ، يتم تقريبها إلى عدد صحيح. يصبح النوع أيضًا نوعًا صحيحًا int.

f = 123.456

print(round(f))
# 123

print(type(round(f)))
# <class 'int'>

إذا تم تحديد الوسيطة الثانية ، فإنها تقوم بإرجاع نوع عائم ذي فاصلة عائمة.

إذا تم تحديد عدد صحيح موجب ، يتم تحديد المكان العشري ؛ إذا تم تحديد عدد صحيح سالب ، يتم تحديد مكان العدد الصحيح. -1 تقرِّب إلى أقرب جزء من عشرة ، و -2 تقريبية لأقرب جزء من مائة ، و 0 تقرب إلى عدد صحيح (المكان الأول) ، لكنها تُرجع نوع عدد عشري ، على عكس ما حدث عندما تم حذفها.

print(round(f, 1))
# 123.5

print(round(f, 2))
# 123.46

print(round(f, -1))
# 120.0

print(round(f, -2))
# 100.0

print(round(f, 0))
# 123.0

print(type(round(f, 0)))
# <class 'float'>

تقريب الأعداد الصحيحة إلى أي عدد من الأرقام.

فيما يلي مثال على معالجة عدد صحيح من نوع int.

إذا تم حذف الوسيطة الثانية ، أو إذا تم تحديد 0 أو عدد صحيح موجب ، يتم إرجاع القيمة الأصلية كما هي. إذا تم تحديد عدد صحيح سالب ، فإنه يتم تقريبه إلى الرقم الصحيح المقابل. في كلتا الحالتين ، يتم إرجاع نوع عدد صحيح.

i = 99518

print(round(i))
# 99518

print(round(i, 2))
# 99518

print(round(i, -1))
# 99520

print(round(i, -2))
# 99500

print(round(i, -3))
# 100000

round () إلى رقم زوجي ، وليس إلى تقريب مشترك

لاحظ أن التقريب باستخدام الدالة round () المضمنة في Python 3 يتم تقريبه إلى رقم زوجي ، وليس إلى تقريب عام.

كما هو مكتوب في الوثائق الرسمية ، يتم تقريب 0.5 إلى 0 ، ويتم تقريب 5 إلى 0 ، وهكذا.

print('0.4 =>', round(0.4))
print('0.5 =>', round(0.5))
print('0.6 =>', round(0.6))
# 0.4 => 0
# 0.5 => 0
# 0.6 => 1

print('4 =>', round(4, -1))
print('5 =>', round(5, -1))
print('6 =>', round(6, -1))
# 4 => 0
# 5 => 0
# 6 => 10

فيما يلي تعريف التقريب إلى رقم زوجي.

إذا كان الكسر أقل من 0.5 ، فقربه لأسفل ؛ إذا كان الكسر أكبر من 0.5 ، قم بتقريبه ؛ إذا كان الكسر 0.5 بالضبط ، فقربه إلى العدد الزوجي بين التقريب لأسفل والتقريب لأعلى.
Rounding – Wikipedia

0.5 لا يتم اقتطاعه دائمًا.

print('0.5 =>', round(0.5))
print('1.5 =>', round(1.5))
print('2.5 =>', round(2.5))
print('3.5 =>', round(3.5))
print('4.5 =>', round(4.5))
# 0.5 => 0
# 1.5 => 2
# 2.5 => 2
# 3.5 => 4
# 4.5 => 4

في بعض الحالات ، لا ينطبق تعريف التقريب إلى رقم زوجي حتى على المعالجة بعد منزلتين عشريتين.

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

ويرجع ذلك إلى حقيقة أنه لا يمكن تمثيل الكسور العشرية كأرقام فاصلة عائمة تمامًا ، كما هو مذكور في الوثائق الرسمية.

قد يفاجئك سلوك الجولة () لأرقام الفاصلة العائمة:على سبيل المثال ، ستعطيك الجولة (2.675، 2) 2.67 بدلاً من 2.68 كما هو متوقع. هذا ليس خطأ.:هذا نتيجة لحقيقة أن معظم الكسور العشرية لا يمكن تمثيلها بالضبط بأرقام الفاصلة العائمة.
round() — Built-in Functions — Python 3.10.2 Documentation

إذا كنت ترغب في تحقيق تقريب عام أو تقريب دقيق للأرقام العشرية إلى أرقام زوجية ، فيمكنك استخدام المكتبة العشرية القياسية (الموصوفة أدناه) أو تحديد وظيفة جديدة.

لاحظ أيضًا أن round () في Python 2 لا يتم تقريبه إلى رقم زوجي ، بل هو تقريب.

تكميم () من المكتبة العشرية القياسية

يمكن استخدام الوحدة العشرية للمكتبة القياسية للتعامل مع أرقام الفاصلة العشرية الدقيقة.

باستخدام طريقة التكميم () للوحدة العشرية ، من الممكن تقريب الأرقام عن طريق تحديد وضع التقريب.

القيم المحددة لتقريب حجة طريقة التكميم () لها المعاني التالية ، على التوالي.

  • ROUND_HALF_UP:التقريب العام
  • ROUND_HALF_EVEN:التقريب لأرقام زوجية

الوحدة العشرية هي مكتبة قياسية ، لذلك لا يلزم تثبيت إضافي ، ولكن الاستيراد ضروري.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

إنشاء كائن عشري

يمكن استخدام Decimal () لإنشاء كائنات من النوع Decimal.

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

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

print(type(Decimal(0.05)))
# <class 'decimal.Decimal'>

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

بما أن 0.5 هي نصف (-1 أس 2) ، فيمكن التعبير عنها بالضبط في شكل ثنائي.

print(Decimal(0.5))
# 0.5

إذا حددت نوع السلسلة str بدلاً من نوع float ، فسيتم التعامل معها كنوع عشري للقيمة الدقيقة.

print(Decimal('0.05'))
# 0.05

تقريب الكسور العشرية لأي عدد من الخانات والتقريب لأرقام زوجية

قم باستدعاء () quantize () من كائن من النوع Decimal لتقريب القيمة.

الوسيطة الأولى لـ quantize () هي سلسلة بها نفس عدد الأرقام مثل عدد الأرقام التي تريد البحث عنها ، مثل “0.1” أو “0.01”.

بالإضافة إلى ذلك ، تحدد الوسيطة ROUNDING وضع التقريب ؛ إذا تم تحديد ROUND_HALF_UP ، فسيتم استخدام التقريب العام.

f = 123.456

print(Decimal(str(f)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 123

print(Decimal(str(f)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP))
# 123.5

print(Decimal(str(f)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 123.46

على عكس جولة الوظيفة المضمنة () ، يتم تقريب 0.5 إلى 1.

print('0.4 =>', Decimal(str(0.4)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.5 =>', Decimal(str(0.5)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.6 =>', Decimal(str(0.6)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 0.4 => 0
# 0.5 => 1
# 0.6 => 1

إذا تم تعيين تقريب الوسيطة إلى ROUND_HALF_EVEN ، فسيتم التقريب إلى أرقام زوجية كما هو الحال في الدالة المضمنة round ().

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

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

print('0.05 =>', Decimal(0.05).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(0.15).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(0.25).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(0.35).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(0.45).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

إذا تم تحديد الوسيطة العشرية () كسلسلة من النوع str ، فسيتم التعامل معها ككائن عشري لتلك القيمة بالضبط ، وبالتالي تكون النتيجة كما هو متوقع.

print('0.05 =>', Decimal(str(0.05)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(str(0.15)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(str(0.25)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(str(0.35)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(str(0.45)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.0
# 0.15 => 0.2
# 0.25 => 0.2
# 0.35 => 0.4
# 0.45 => 0.4

نظرًا لأنه يمكن معالجة 0.5 بشكل صحيح بواسطة نوع float ، فلا توجد مشكلة في تحديد نوع float باعتباره وسيطة Decimal () عند التقريب إلى عدد صحيح ، ولكن من الأكثر أمانًا تحديد نوع السلسلة str عند التقريب إلى منزلة عشرية.

على سبيل المثال ، 2.675 هو في الواقع 2.67499 …. في نوع عائم. لذلك ، إذا كنت تريد التقريب إلى منزلتين عشريتين ، فيجب عليك تحديد سلسلة إلى عشري () ، وإلا فإن النتيجة ستكون مختلفة عن النتيجة المتوقعة سواء قمت بالتقريب إلى أقرب رقم صحيح (ROUND_HALF_UP) أو إلى رقم زوجي (ROUND_HALF_EVEN ).

print(Decimal(2.675))
# 2.67499999999999982236431605997495353221893310546875

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.68

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.68

لاحظ أن طريقة quantize () ترجع رقم نوع عشري ، لذلك إذا كنت تريد العمل على رقم نوع عائم ، فأنت بحاجة إلى تحويله إلى نوع عائم باستخدام float () ، وإلا سيحدث خطأ.

d = Decimal('123.456').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)

print(d)
# 123.46

print(type(d))
# <class 'decimal.Decimal'>

# print(1.2 + d)
# TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal'

print(1.2 + float(d))
# 124.66

تقريب الأعداد الصحيحة إلى أي عدد من الخانات والتقريب لأرقام زوجية

إذا كنت تريد التقريب إلى رقم صحيح ، فإن تحديد شيء مثل “10” باعتباره الوسيطة الأولى لن يمنحك النتيجة المرجوة.

i = 99518

print(Decimal(i).quantize(Decimal('10'), rounding=ROUND_HALF_UP))
# 99518

هذا لأن التكميم () يؤدي التقريب وفقًا لأس الكائن العشري ، لكن الأس العشري (’10 ‘) هو 0 ، وليس 1.

يمكنك تحديد أس عشوائي باستخدام E كسلسلة أس (على سبيل المثال ، “1E1”). يمكن التحقق من الأس في طريقة as_tuple.

print(Decimal('10').as_tuple())
# DecimalTuple(sign=0, digits=(1, 0), exponent=0)

print(Decimal('1E1').as_tuple())
# DecimalTuple(sign=0, digits=(1,), exponent=1)

كما هي ، ستكون النتيجة في شكل تدوين أسي باستخدام E. إذا كنت تريد استخدام التدوين العادي ، أو إذا كنت تريد العمل باستخدام عدد صحيح من النوع int بعد التقريب ، فاستخدم int () لتحويل النتيجة.

print(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))
# 9.952E+4

print(int(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 99520

print(int(Decimal(i).quantize(Decimal('1E2'), rounding=ROUND_HALF_UP)))
# 99500

print(int(Decimal(i).quantize(Decimal('1E3'), rounding=ROUND_HALF_UP)))
# 100000

إذا تم تعيين تقريب الوسيطة إلى ROUND_HALF_UP ، فسيحدث تقريب عام ، على سبيل المثال ، سيتم تقريب 5 إلى 10.

print('4 =>', int(Decimal(4).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('5 =>', int(Decimal(5).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('6 =>', int(Decimal(6).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 4 => 0
# 5 => 10
# 6 => 10

بالطبع ، لا توجد مشكلة إذا حددتها كسلسلة.

حدد وظيفة جديدة

طريقة استخدام الوحدة العشرية دقيقة وآمنة ، ولكن إذا لم تكن مرتاحًا لتحويل النوع ، فيمكنك تحديد وظيفة جديدة لتحقيق التقريب العام.

هناك العديد من الطرق الممكنة للقيام بذلك ، على سبيل المثال ، الوظيفة التالية.

def my_round(val, digit=0):
    p = 10 ** digit
    return (val * p * 2 + 1) // 2 / p

إذا لم تكن بحاجة إلى تحديد عدد الأرقام والتقريب دائمًا إلى أول منزلة عشرية ، يمكنك استخدام صيغة أبسط.

my_round_int = lambda x: int((x * 2 + 1) // 2)

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

ما يلي هو للإشارة فقط.

تقريب الكسور العشرية إلى أي عدد من الأرقام.

print(int(my_round(f)))
# 123

print(my_round_int(f))
# 123

print(my_round(f, 1))
# 123.5

print(my_round(f, 2))
# 123.46

على عكس الجولة ، 0.5 تصبح 1 حسب التقريب العام.

print(int(my_round(0.4)))
print(int(my_round(0.5)))
print(int(my_round(0.6)))
# 0
# 1
# 1

تقريب الأعداد الصحيحة إلى أي عدد من الأرقام

i = 99518

print(int(my_round(i, -1)))
# 99520

print(int(my_round(i, -2)))
# 99500

print(int(my_round(i, -3)))
# 100000

على عكس الجولة ، 5 تصبح 10 حسب التقريب العام.

print(int(my_round(4, -1)))
print(int(my_round(5, -1)))
print(int(my_round(6, -1)))
# 0
# 10
# 10

ملاحظة: للقيم السالبة

في المثال أعلاه ، تم تقريب -0.5 إلى 0.

print(int(my_round(-0.4)))
print(int(my_round(-0.5)))
print(int(my_round(-0.6)))
# 0
# 0
# -1

هناك طرق مختلفة للتفكير في تقريب القيم السالبة ، ولكن إذا كنت تريد جعل -0.5 في -1 ، فيمكنك تعديلها على النحو التالي ، على سبيل المثال

import math

def my_round2(val, digit=0):
    p = 10 ** digit
    s = math.copysign(1, val)
    return (s * val * p * 2 + 1) // 2 / p * s

print(int(my_round2(-0.4)))
print(int(my_round2(-0.5)))
print(int(my_round2(-0.6)))
# 0
# -1
# -1