حساب الدقة التعسفية في إرلانغ


rawpixel


حتى تلاميذ المدارس يدركون وجود أنظمة أعداد مختلفة وحقيقة أنه ليس كل كسر عشري محدود هو جزء محدود في نظام الأعداد الثنائية. قلة من الناس يعتقدون أنه بسبب هذه الحقيقة ، فإن عمليات التعويم والمضاعفة ليست دقيقة.


إذا تحدثنا عن Erlang ، فعندئذٍ ، مثل العديد من اللغات الأخرى ، فإنه يطبق معيار IEEE754 للطفو ، بينما يتم تنفيذ نوع Integer القياسي في Erlang باستخدام الحساب الدقيق التعسفي. ومع ذلك ، أود أن لا أملك فقط bigint ، ولكن أيضًا القدرة على العمل بأرقام عقلانية ومعقدة وفاصلة عائمة بالدقة اللازمة.


تقدم المقالة نظرة عامة بسيطة على نظرية ترميز أرقام الفاصلة العائمة والأمثلة الأكثر إثارة للانتباه للتأثيرات الناشئة. تم تصميم الحل الذي يوفر الدقة اللازمة للعمليات من خلال الانتقال إلى تمثيل ثابت ، كمكتبة EAPA (حساب Erlang Arbitrary Precision Arithmetic) ، مصمم لتلبية احتياجات التطبيقات المالية التي تم تطويرها على Erlang / Elixir.




المعايير والمعايير والمعايير ...


اليوم ، المعيار الرئيسي لحسابات النقطة العائمة الثنائية هو IEEE754 ، المستخدم على نطاق واسع في الهندسة والبرمجة. يحدد أربعة أشكال للعرض:


  • دقة واحدة 32 بت
  • دقة مزدوجة 64 بت
  • دقة واحدة ممتدة> = 43 بت (نادرًا ما تستخدم)
  • دقة موسعة مزدوجة> = 79 بت (عادةً ما تستخدم 80 بت)
    وأربعة أوضاع للتقريب:
  • التقريب ، يميل إلى أقرب كل.
  • التقريب يميل إلى الصفر.
  • التقريب يميل إلى + ∞
  • التقريب نحو -∞

يتم تصنيع معظم المعالجات الدقيقة الحديثة باستخدام الأجهزة لتمثيل المتغيرات الحقيقية بتنسيق IEEE754. تحدد تنسيقات العروض التقديمية حد الحجم لعدد ، وتؤثر أوضاع التقريب على الدقة. لا يستطيع المبرمجون غالبًا تغيير سلوك الأجهزة وتنفيذ لغات البرمجة. على سبيل المثال ، يخزن تطبيق Erlang الرسمي تعويمًا في 3 كلمات على جهاز 64 بت و 4 كلمات على 32 بت.


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


يحتوي الجزء الأكبر من الأرقام عند عرضه على مجموعة محدودة على خطأ نسبي ثابت وصغير. لذلك ، بالنسبة للعوامة تكون 11.920928955078125e-6٪ ، وللمضاعفة - 2.2204460492503130808472633361816e-14٪. في حياة المبرمجين ، تسمح لنا معظم المهام اليومية التي يمكن حلها بإهمال هذا الخطأ ، على الرغم من أنه يجب ملاحظة أنه حتى في المهام البسيطة ، يمكنك التقدم على أشعل النار ، لأن حجم الخطأ المطلق يمكن أن يصل إلى 10 31 و 10 292 للطفو والمضاعفة ، على التوالي ، مما يسبب صعوبات في الحسابات.


توضيح الآثار


من المعلومات العامة إلى الأعمال. دعونا نحاول استنساخ الآثار الناشئة في إرلانغ.


تم تصميم جميع الأمثلة أدناه على أنها اختبارات ct.


التقريب وفقدان الدقة


لنبدأ مع الكلاسيكيات - إضافة رقمين: 0.1 + 0.2 = ؟:


t30000000000000004(_)-> ["0.30000000000000004"] = io_lib:format("~w", [0.1 + 0.2]). 

تختلف نتيجة الإضافة قليلاً عن تلك المتوقعة بشكل حدسي ، ويمر الاختبار بنجاح. دعونا نحاول تحقيق النتيجة الصحيحة. أعد كتابة الاختبار باستخدام EAPA:


 t30000000000000004_eapa(_)-> %% prec = 1 symbols after coma X = eapa_int:with_val(1, <<"0.1">>), Y = eapa_int:with_val(1, <<"0.2">>), <<"0.3">> = eapa_int:to_float(1, eapa_int:add(X, Y)). 

نجح هذا الاختبار أيضًا ، مما يدل على أنه تم حل المشكلة.
دعنا نواصل التجارب ، نضيف قيمة صغيرة جدًا إلى 1.0:


 tiny(_)-> X = 1.0, Y = 0.0000000000000000000000001, 1.0 = X + Y. 

كما ترون ، مرت زيادة لدينا دون أن يلاحظها أحد. نحاول إصلاح المشكلة ، مع توضيح إحدى ميزات المكتبة في الوقت نفسه - التحجيم التلقائي:


 tiny_eapa(_)-> X1 = eapa_int:with_val(1, <<"1.0">>), X2 = eapa_int:with_val(25, <<"0.0000000000000000000000001">>), <<"1.0000000000000000000000001">> = eapa_int:to_float(eapa_int:add(X1, X2)). 

بت شبكة تجاوز الحد


بالإضافة إلى المشكلات المرتبطة بالأعداد الصغيرة ، يعد تجاوز السعة مشكلة واضحة وهامة.


 float_overflow(_) -> 1.0 = 9007199254740991.0 - 9007199254740990.0, 1.0 = 9007199254740992.0 - 9007199254740991.0, 0.0 = 9007199254740993.0 - 9007199254740992.0, 2.0 = 9007199254740994.0 - 9007199254740993.0. 

كما ترى من الاختبار ، عند نقطة ما يتوقف الفرق عن أن يكون مساوياً لـ 1.0 ، والتي من الواضح أنها مشكلة. يحل EAPA أيضًا هذه المشكلة:


 float_overflow_eapa(_)-> X11 = eapa_int:with_val(1, <<"9007199254740992.0">>), X21 = eapa_int:with_val(1, <<"9007199254740991.0">>), <<"1.0">> = eapa_int:to_float(1, eapa_int:sub(X11, X21)), X12 = eapa_int:with_val(1, <<"9007199254740993.0">>), X22 = eapa_int:with_val(1, <<"9007199254740992.0">>), <<"1.0">> = eapa_int:to_float(1, eapa_int:sub(X12, X22)), X13 = eapa_int:with_val(1, <<"9007199254740994.0">>), X23 = eapa_int:with_val(1, <<"9007199254740993.0">>), <<"1.0">> = eapa_int:to_float(1, eapa_int:sub(X13, X23)). 

تخفيض خطير


يوضح الاختبار التالي حدوث انخفاض خطير. ويصاحب هذه العملية انخفاض كارثي في ​​دقة الحسابات في العمليات حيث تكون القيمة الناتجة أقل بكثير من المدخلات. في حالتنا ، نتيجة الطرح 1.


نظهر أن هذه المشكلة موجودة في Erlang:


 reduction(_)-> X = float(87654321098765432), Y = float(87654321098765431), 16.0 = XY. %% has to be 1.0 

اتضح 16.0 بدلاً من 1.0 المتوقع. لنحاول إصلاح هذا الموقف:


 reduction_eapa(_)-> X = eapa_int:with_val(1, <<"87654321098765432">>), Y = eapa_int:with_val(1, <<"87654321098765431">>), <<"1.0">> = eapa_int:to_float(eapa_int:sub(X, Y)). 

الميزات الأخرى لحساب النقطة العائمة في إرلانغ


لنبدأ بتجاهل الصفر السالب.


 eq(_)-> true = list_to_float("0.0") =:= list_to_float("-0.0"). 

أريد فقط أن أقول أن EAPA تحتفظ بهذا السلوك:


 eq_eapa(_)-> X = eapa_int:with_val(1, <<"0.0">>), Y = eapa_int:with_val(1, <<"-0.0">>), true = eapa_int:eq(X, Y). 

لأنه صالح. ليس لدى Erlang بنية واضحة ومعالجة NaN واللانهاية ، مما يؤدي إلى عدد من الميزات ، على سبيل المثال ، هذه:


 1> math:sqrt(list_to_float("-0.0")). 0.0 

النقطة التالية هي ميزة معالجة أعداد كبيرة وصغيرة. دعونا نحاول التكاثر للصغار:


 2> list_to_float("0."++lists:duplicate(322, $0)++"1"). 1.0e-323 3> list_to_float("0."++lists:duplicate(323, $0)++"1"). 0.0 

وللأعداد الكبيرة:


 4> list_to_float("1"++lists:duplicate(308, $0)++".0"). 1.0e308 5> list_to_float("1"++lists:duplicate(309, $0)++".0"). ** exception error: bad argument 

في ما يلي بعض الأمثلة الأخرى للأرقام الصغيرة:


 6> list_to_float("0."++lists:duplicate(322, $0)++"123456789"). 1.0e-323 7> list_to_float("0."++lists:duplicate(300, $0)++"123456789"). 1.23456789e-301 

 8> 0.123456789e-100 * 0.123456789e-100. 1.524157875019052e-202 9> 0.123456789e-200 * 0.123456789e-200. 0.0 

تؤكد الأمثلة أعلاه حقيقة مشاريع Erlang: لا يمكن احتساب الأموال في IEEE754.


EAPA (حساب إرلانج التعسفي - الدقة)


EAPA عبارة عن ملحق NIF مكتوب بلغة Rust. في الوقت الحالي ، يوفر مستودع EAPA واجهة eapa_int الأكثر بساطة وسهولة للعمل مع أرقام النقطة الثابتة. تتضمن ميزات eapa_int ما يلي:


  1. عدم وجود تأثيرات الترميز IEEE754
  2. دعم أعداد كبيرة
  3. دقة قابلة للتكوين حتى 126 منزلاً عشريًا. (في التنفيذ الحالي)
  4. أوتوسكالينج
  5. دعم جميع العمليات الأساسية على الأرقام
  6. أكثر أو أقل اختبار كامل ، بما في ذلك الملكية القائمة.

واجهة eapa_int :


  • with_val/2 - ترجمة رقم with_val/2 العائمة إلى تمثيل ثابت ، والذي يمكن استخدامه ، بما في ذلك بأمان ، في json ، xml.
  • to_float/2 - ترجمة رقم نقطة ثابتة إلى رقم نقطة عائمة بدقة معينة.
  • to_float/1 - ترجمة رقم نقطة ثابتة إلى رقم نقطة عائمة.
  • add/2 - مجموع رقمين
  • sub/2 - الفرق
  • mul/2 - الضرب
  • divp/2 - القسمة
  • min/2 - الحد الأدنى من الأرقام
  • max/2 - الحد الأقصى للأرقام
  • eq/2 - تحقق من المساواة في الأرقام
  • lt/2 - تحقق من أن الرقم أقل
  • lte/2 - فحص أقل من يساوي
  • gt/2 - تحقق من أن الرقم أكبر
  • gte/2 - التدقيق أكثر من المساواة

يمكن العثور على كود EAPA في المستودع https://github.com/Vonmo/eapa


متى يجب عليك استخدام eapa_int؟ على سبيل المثال ، إذا كان التطبيق الخاص بك يعمل بالمال أو كنت بحاجة إلى إجراء عمليات حسابية بشكل ملائم ودقيق على أرقام مثل 92233720368547758079223372036854775807.92233720368547758079223372036854775807 ، يمكنك استخدام EAPA بأمان.


مثل أي حل ، EAPA حل وسط. نحصل على الدقة اللازمة من خلال التضحية بالذاكرة والسرعة الحسابية.تظهر اختبارات الأداء والإحصائيات التي تم جمعها على الأنظمة الحقيقية أن معظم العمليات تتم في نطاق 3-30 ميكرون يجب أيضًا مراعاة هذه النقطة عند اختيار واجهة نقطة ثابتة لـ EAPA.


الخلاصة


بالطبع ، ليس من الضروري دائمًا حل مثل هذه المشاكل في Erlang أو Elixir ، ولكن عندما تنشأ مشكلة ولا يتم العثور على أداة مناسبة ، يجب عليك اختراع حل.
هذه المقالة هي محاولة لمشاركة المجتمع مع الأداة والخبرة ، على أمل أن تكون هذه المكتبة مفيدة لبعض الناس وتساعد في توفير الوقت.
ما رأيك في المال في ارلانغ؟


ملاحظة: سيتم تغطية العمل بأرقام منطقية ومعقدة ، بالإضافة إلى وصول أصلي إلى أنواع Integer و Float و Complex و Rational من الدقة التعسفية في المنشورات التالية. لا تقم بالتبديل!




المواد ذات الصلة:


Source: https://habr.com/ru/post/ar428111/


All Articles