اختبار ما إذا كان رقم الفاصلة العائمة عددًا صحيحًا

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

  •  02-07-2019
  •  | 
  •  

سؤال

يعمل هذا الكود (C#3)

double d;
if(d == (double)(int)d) ...;
  1. هل هناك طريقة أفضل للقيام بذلك؟
  2. لأسباب خارجية أريد أن أتجنب التمثيل المزدوج؛ما هي الطرق اللطيفة الموجودة بخلاف هذا؟(حتى لو لم تكن جيدة)

ملحوظة: أشار العديد من الأشخاص إلى النقطة (المهمة) وهي أن == غالبًا ما تكون مشكلة في تحديد النقطة العائمة.في هذه الحالات أتوقع قيمًا تتراوح بين 0 إلى بضع مئات ومن المفترض أن تكون أعدادًا صحيحة (غير ints هي أخطاء) لذلك إذا كانت هذه النقاط "لا ينبغي" أن تكون مشكلة بالنسبة لي.

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

المحلول

d == Math.Floor(d)

يفعل نفس الشيء وبعبارة أخرى.

ملحوظة:نأمل أن تكون مدركًا أنه يجب عليك توخي الحذر الشديد عند القيام بهذا النوع من الأشياء؛من السهل جدًا أن تتراكم العوامات/الزوجي الأخطاء الصغيرة التي تجعل المقارنات الدقيقة (مثل هذه) تفشل دون سبب واضح.

نصائح أخرى

إذا كان ضعفك نتيجة لعملية حسابية أخرى، فربما تريد شيئًا مثل:

d == Math.Floor(d + 0.00001);

بهذه الطريقة، إذا كان هناك خطأ بسيط في التقريب، فسيظل الرقم مطابقًا.

هذا من شأنه أن يعمل على ما أعتقد:

if (d % 1 == 0) {
  //...
}

لا أستطيع الإجابة على الجزء الخاص بـ C# من السؤال، ولكن يجب أن أشير إلى أنك ربما تفتقد مشكلة عامة تتعلق بأرقام الفاصلة العائمة.

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

على سبيل المثال، 1.1 + 0.6 != 1.7.

نعم، هذه هي الطريقة التي تعمل بها أرقام الفاصلة العائمة.

هنا، 1.1 + 0.6 - 1.7 == 2.2204460492503131e-16.

بالمعنى الدقيق للكلمة، أقرب شيء لمقارنة المساواة التي يمكنك القيام بها مع العوامات هو مقارنتها حتى الدقة المختارة.

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

إذا كنت ستقوم بتحويله فقط، فإن إجابة Mike F/Koth جيدة، ولكنها لا تجيب على سؤالك تمامًا.إذا كنت ستقوم بالاختبار فعليًا، وهو أمر مهم بالفعل، فإنني أوصيك بتنفيذ شيء يتضمن هامشًا من الخطأ.

على سبيل المثال، إذا كنت تفكر في المال وتريد اختبار المبالغ الزوجية بالدولار، فيمكنك أن تقول (باتباع نمط كوث):

if( Math.abs(d - Math.Floor(d + 0.001)) < 0.001)

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

لا تحتاج إلى الإضافية (المزدوجة) هناك.هذا يعمل:

if (d == (int)d) {
 //...
}

استخدم Math.Truncate()

اختبار بسيط مثل 'x == Floor(x)' مضمون رياضيًا للعمل بشكل صحيح، لأي رقم FP ذو دقة ثابتة.

تمثل جميع ترميزات FP القانونية ذات الدقة الثابتة أرقامًا حقيقية مميزة، وبالتالي لكل عدد صحيح x، يوجد على الأكثر ترميز FP واحد ثابت الدقة يطابقه تمامًا.

لذلك، لكل عدد صحيح x يمكن تمثيله بهذه الطريقة، لدينا x == Floor(x) بالضرورة، نظرًا لأن الكلمة (x) حسب التعريف تُرجع أكبر رقم FP y بحيث يمثل y <= x وy عددًا صحيحًا؛لذلك يجب أن يُرجع الطابق (x) x.

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

static void Main(string[] args)
{
    const int precision = 10000;

    foreach (var d in new[] { 2, 2.9, 2.001, 1.999, 1.99999999, 2.00000001 })
    {
        if ((int) (d*precision + .5)%precision == 0)
        {
            Console.WriteLine("{0} is an int", d);
        }
    }
}

والإخراج هو

2 is an int
1.99999999 is an int
2.00000001 is an int

شيء من هذا القبيل

double d = 4.0;
int i = 4;

bool equal = d.CompareTo(i) == 0; // true

هل يمكنك استخدام هذا

    bool IsInt(double x)
    {
        try
        {
            int y = Int16.Parse(x.ToString());
            return true;
        }
        catch 
        {
            return false;
        }
    }

للتعامل مع دقة مزدوجة...

Math.Abs(d - Math.Floor(d)) <= double.Epsilon

خذ بعين الاعتبار الحالة التالية حيث القيمة أقل من الضعف. إبسيلون فشل في المقارنة مع الصفر.

// number of possible rounds
const int rounds = 1;

// precision causes rounding up to double.Epsilon
double d = double.Epsilon*.75;

// due to the rounding this comparison fails
Console.WriteLine(d == Math.Floor(d));

// this comparison succeeds by accounting for the rounding
Console.WriteLine(Math.Abs(d - Math.Floor(d)) <= rounds*double.Epsilon);

// The difference is double.Epsilon, 4.940656458412465E-324
Console.WriteLine(Math.Abs(d - Math.Floor(d)).ToString("E15"));
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top