هل من الآمن التحقق من قيم الفاصلة العائمة للمساواة مع 0؟

StackOverflow https://stackoverflow.com/questions/485175

سؤال

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

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

لكني لا أعرف الكثير عن هذا الموضوع، لذا ليس من حقي أن أقول ذلك.

double x = 0.0;
return (x == 0.0) ? true : false;

هل سيعود ذلك دائمًا صحيحًا؟

هل كانت مفيدة؟

المحلول

إنها آمن لنتوقع أن المقارنة سوف تعود true إذا وفقط إذا كان المتغير المزدوج له قيمة بالضبط 0.0 (وهذا هو الحال بالطبع في مقتطف الشفرة الأصلي).وهذا يتفق مع دلالات الحديث == المشغل أو العامل. a == b وسائل "a مساوي ل b".

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

نصائح أخرى

إذا كنت بحاجة إلى أن تفعل الكثير من المقارنات "المساواة" قد تكون فكرة جيدة لكتابة وظيفة مساعد قليلا أو طريقة التمديد في .NET Framework 3.5 لمقارنة:

public static bool AlmostEquals(this double double1, double double2, double precision)
{
    return (Math.Abs(double1 - double2) <= precision);
}

ويمكن استخدام هذه الطريقة التالية:

double d1 = 10.0 * .1;
bool equals = d1.AlmostEquals(0.0, 0.0000001);

لعينتك بسيطة، هذا الاختبار على ما يرام. ولكن ماذا عن هذا:

bool b = ( 10.0 * .1 - 1.0 == 0.0 );

وتذكر أن 0.1 هو تكرار عشري في ثنائي ولا يمكن تمثيل بالضبط. ثم قارن ذلك إلى هذا الرمز:

double d1 = 10.0 * .1; // make sure the compiler hasn't optimized the .1 issue away
bool b = ( d1 - 1.0 == 0.0 );

وسأترك لك لتشغيل اختبار لمعرفة النتائج الفعلية: كنت أكثر من المرجح أن نتذكر أن الطريق

.

من إدخال MSDN لـ مزدوج. يساوي:

الدقة في المقارنات

يجب استخدام طريقة متساوية بحذر ، لأن قيمتان مكافئتان على ما يبدو يمكن أن يكونا غير متكافئين بسبب الدقة المختلفة للقيمتين.يوضح المثال التالي أن القيمة المزدوجة .3333 والمزدوجة التي تم إرجاعها عن طريق تقسيم 1 على 3 غير متكافئة.

...

بدلاً من مقارنة المساواة ، تتضمن تقنية موصى بها تحديد هامش مقبول من الفرق بين قيمتين (مثل .01 ٪ من إحدى القيم).إذا كانت القيمة المطلقة للفرق بين القيمتين أقل من أو تساوي هذا الهامش ، فمن المحتمل أن يكون الفرق بسبب الاختلافات في الدقة ، وبالتالي ، من المحتمل أن تكون القيم متساوية.يستخدم المثال التالي هذه التقنية لمقارنة .33333 و 1/3 ، وهما القيمتان المزدوجتان اللذان تم العثور على مثال الكود السابق ليكون غير متكافئ.

انظر أيضا مزدوج إبسيلون.

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

float f = 0.1F;
bool b1 = (f == 0.1); //returns false
bool b2 = (f == 0.1F); //returns true

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

إذا تم تعيين عدد مباشرة إلى تعويم أو مزدوج فمن آمن لاختبار مقابل صفر أو أي عدد صحيح يمكن تمثيلها في 53 بت لضعف أو 24 بت لتعويم.

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

ويمكنك أيضا البدء عن طريق تعيين عدد صحيح ولها تواصل مقارنات بسيطة للعمل عن طريق التمسك مضيفا طرح أو ضرب من قبل الأعداد الصحيحة (على افتراض والنتيجة هي أقل من 24 بت لتعويم عبد 53 بت لنوع بيانات مزدوج) . حتى تتمكن من علاج يطفو والزوجي أنها أعداد صحيحة تحت ظروف معينة.

لا، فمن غير موافق. أن ما يسمى القيم بدون تسوية (دون السوية)، بالمقارنة يساوي 0.0، مقارنة كما كاذبة (غير صفر)، ولكن عند استخدامها في معادلة سيتم تطبيع (تصبح 0.0). وهكذا، وذلك باستخدام هذا كآلية لتجنب قسمة الصفر ليست آمنة. بدلا من ذلك، إضافة 1.0 و 1.0 مقارنة. وهذا يضمن أن يتم التعامل مع كل subnormals صفر.

وجرب هذا، وسوف تجد أن == لا يمكن الاعتماد عليه لضعف / تعويم.
double d = 0.1 + 0.2; bool b = d == 0.3;

وهنا في الجواب من قرة.

والواقع، وأعتقد أنه من الأفضل استخدام الرموز التالية لمقارنة قيمة مزدوجة ضد 0.0:

double x = 0.0;
return (Math.Abs(x) < double.Epsilon) ? true : false;

ونفس لتعويم:

float x = 0.0f;
return (Math.Abs(x) < float.Epsilon) ? true : false;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top