سؤال

لجعل المشكلة قصيرة ، دعنا نقول أنني أريد حساب التعبير a / (b - c) على floatس.

للتأكد من أن النتيجة ذات مغزى ، يمكنني التحقق من ما إذا b و c على قدم المساواة:

float EPS = std::numeric_limits<float>::epsilon();
if ((b - c) > EPS || (c - b) > EPS)
{
    return a / (b - c);
}

لكن اختباراتي تظهر أنه لا يكفي ضمان نتائج ذات مغزى ولا تفشل في تقديم نتيجة إذا كان ذلك ممكنًا.

حالة 1:

a = 1.0f;
b = 0.00000003f;
c = 0.00000002f;

نتيجة: إذا لم يتم استيفاء الشرط إذا كان التعبير ينتج عنه نتيجة صحيحة 100000008 (كما هو الحال بالنسبة لدقة العوامات).

الحالة 2:

a = 1e33f;
b = 0.000003;
c = 0.000002;

نتيجة: تم استيفاء حالة if ، لكن التعبير لا ينتج نتيجة ذات معنى +1.#INF00.

لقد وجدت أنه أكثر موثوقية للتحقق من النتيجة ، وليس الحجج:

const float INF = numeric_limits<float>::infinity();
float x = a / (b - c);
if (-INF < x && x < INF)
{
     return x;
}

ولكن ما الذي هو إبسيلون إذن ولماذا يقول الجميع أن إبسيلون جيد للاستخدام؟

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

المحلول

"يجب عليك استخدام epsilon عند التعامل مع العوامات" هو رد فعل على الركبة للمبرمجين مع فهم سطحي للحسابات الفاصلة ، للمقارنات بشكل عام (ليس فقط الصفر).

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

إذا لم تقرأ ما يجب أن يعرفه كل عالم كمبيوتر عن الحساب العائم, ، إنها نقطة انطلاق جيدة. أبعد من ذلك ، إذا كنت مهتمًا بدقة نتيجة القسم في مثالك ، فيجب عليك تقدير مدى عدم الدقة b-c صنع من قبل السابق تقريب الأخطاء ، لأنه في الواقع إذا b-c صغير ، خطأ مطلق صغير يتوافق مع خطأ مطلق كبير في النتيجة. إذا كان اهتمامك هو فقط أن القسم يجب ألا يفيض ، فإن الاختبار (في النتيجة) صحيح. لا يوجد سبب لاختبار مقسوم فارغ ذو أرقام عائمة ، فأنت مجرد اختبار لتدفق النتيجة ، والذي يلتقط كل من الحالات التي يكون فيها المقسوم فارغًا وحيث يكون المقسوم صغيرًا جدًا بحيث لا يتم تمثيل النتيجة مع أي دقة.

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

نصائح أخرى

يتم استخدام Epsilon لتحديد ما إذا كان رقمين خاضعين لخطأ التقريب قريبان بما يكفي ليعتبر "متساويًا". لاحظ أنه من الأفضل الاختبار fabs(b/c - 1) < EPS من fabs(b-c) < EPS, وأفضل - بفضل تصميم عوامات IEEE - للاختبار abs(*(int*)&b - *(int*)&c) < EPSI (حيث EPSI هو بعض عدد صحيح صغير).

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top