قد لا يزال NaN يفاجئك قليلاً

صورة



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



يجب أن تكون الإجابة NaN. لكن لماذا لست متأكدا من هذا؟ على طول الطريق ، كنت واثقًا من أن أي تعبيرات تحتوي على NaN ستعيد NaN. حسنًا ، ربما فقط إذا قسمت NaN على صفر - في هذه الحالة ، سيتم طرح استثناء ZeroDivisionError . مائة بالمائة NaN!



أقوم بإدخال التعبير في خلية المفكرة:



>>> 1**nan + 1**nan
2.0


في الواقع؟ انتظر:



>>> arange(5)**nan
array([nan,  1., nan, nan, nan])


وهذا يعني ، لسبب ما ، أن واحدًا مرفوعًا لقوة NaN هو واحد ، لكن صفرًا وجميع الأعداد الأخرى للقوة NaN هي NaN. أين المنطق؟ ما هو الأمر؟



لنعد مرة أخرى:



>>> 0**nan, 1**nan
(nan, 1.0)


ربما كنت فقط بسبب عدم وجود بعض الحاجة العملية للمعرفة العميقة بـ NaN ، لم أشك في شيء ما؟ أو ربما عرفت ولكن نسيت؟ أو ربما أسوأ - لم أكن أعلم ونسيت؟



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



>>> hypot(inf, nan)
inf


على الرغم من أنه في نفس الوقت:



>>> sqrt(inf**2 + nan**2)
nan


هذا ، كما ترى ، غريب بعض الشيء.



حسنًا ، من ويكيبيديا ننتقل إلى C99 في الصفحة 182 وأخيراً نحصل على تفسير منطقي لسبب إرجاع pow (x ، 0) 1 لأي x ، حتى بالنسبة إلى x يساوي NaN:



>>> power(nan, 0)
1.0


إذا كانت الوظيفة F(x) يرفع إلى السلطة ز(x) وحيث ز(x) يميل إلى 0 ، ثم ستكون النتيجة 1 ، بغض النظر عن القيمة F(x)...



صورة



وإذا كانت النتيجة لا تعتمد على القيمة العددية للدالةF(x)، إذن 1 نتيجة صحيحة ، حتى بالنسبة لـ NaN. ومع ذلك ، لا يزال هذا لا يفسر لماذا 1 إلى قوة NaN هي 1.



نبحث عن C99 أخرى وفي الصفحة 461 لا نرى أي تفسير ، فقط شرط أن pow (+1 ، y) يجب أن يعيد 1 لكل y ، حتى متساوي ن. كل شىء.



من ناحية أخرى ، شرح سبب تفضيل pow (NaN ، 0) = 1 على pow (NaN ، 0) = لا يزال NaN يشير إلى أنه لا ينبغي اعتبار NaN حرفيًا على أنه ليس رقمًا ... لنفترض أنه نتيجة لبعض العمليات الحسابية حصلنا على رقم يتجاوز حجم الذاكرة المخصص لهذا النوع من الأرقام ، على سبيل المثال:



>>> a = pi*10e307
>>> a
inf


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

>>> b = e*10e307
>>> b
inf


سيعود الفرق بين a و b إلى NaN:



>>> c = a - b
>>> c
nan


السبب الوحيد الذي يجعلنا نعد c ليس عددًا هو أننا لم نستخدم حسابات دقيقة كافية. ومع ذلك ، في c ، تحت NaN ، لا يزال هناك بعض المعنى المخفي. نحن لا نعرف ما هو هذا المعنى. لكنه لا يزال رقمًا ، وبما أنه رقم ، فلا عجب في حقيقة أن pow (1، NaN) = 1 .



لماذا إذن الأسرى (0 ، NaN) = NaN ؟ الحقيقة هي أننا إذا رفعنا 0 إلى أي قوة ، فسنحصل على صفر حقًا. باستثناء حالة واحدة - عندما تكون الدرجة 0:



>>> 0**0
1


لهذا السبب ، هناك غموض في التعبير pow (0 ، NaN) بقيمة NaN محددة. بطبيعة الحال ، فإن احتمال إخفاء 0 تحت NaN ضئيل للغاية ، ويمكن للمرء أن يفترض أن pow (0 ، NaN) = 0 . لكن لا يزال من الأفضل تشغيلها بأمان ، فأنت لا تعرف أبدًا ما يمكن أن يؤدي إليه هذا. ربما كانت هذه هي الطريقة التي فكروا بها عندما تم إنشاء المعايير.



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



ملاحظة: نظرًا لأن NaN يشير إلى أرقام الفاصلة العائمة ، فيمكن أن يكون مفتاح قاموس:



>>> d = {0.1: 'a', nan: 'b'}
>>> d[nan]
'b'


هل يعقل استخدام هذا في الممارسة؟ لا أعتقد أن الأمر يستحق ذلك.



All Articles