سؤال

لقد كتبت طريقة لتحويل رقم معين من أيام إلى ميلي ثانية:

private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = expireTimeInDays * 24 * 60 * 60 * 1000;
}

لقد واجهت صعوبة في معرفة الخطأ الذي ارتكبته.والآن سؤالي:فهل هذا الخطأ واضح إلى هذه الدرجة؟

الطريقة المصححة:

private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = ((long) expireTimeInDays) * 24 * 60 * 60 * 1000;
}

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

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

المحلول

هل هذا واضح؟أعتقد أن ذلك يعتمد على مدة استخدامك لـ Java وعدد المرات التي كان عليك فيها التعامل مع المللي ثانية.وبطبيعة الحال، ينبغي أن يكون على ما يرام لمدة تصل إلى حوالي 24 يوما ...

أعتقد أن التلميح الأكبر يجب أن يكون ذلك System.currentTimeMillis() يعود أ long.وهذا مؤشر جيد على أن عددًا من المللي ثانية يمكن أن يصبح كبيرًا.يجب أن يكون نوع المتغير الذي تقوم بتعيينه بمثابة تلميح جيد أيضًا.

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

نصائح أخرى

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

متغير المعامل الخاص بك والأرقام الحرفية من النوع int.يحتوي نوع البيانات int على قيمة قصوى تبلغ 2^31 -1.لذلك، مع هذه الأعداد الكبيرة، يفيض نوع البيانات int مما يؤدي إلى إجابة تبدو غير صحيحة.

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

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

قد تكون مهتمًا بمعرفة أن هذا تمت تغطيته في "Java Puzzlers" بقلم جوشوا بلوخ ونيل جافتر.

alt text
(مصدر: javapuzzlers.com)

سوف تجد العديد من المخاطر والفخاخ وحالات الزاوية الأخرى في هذا الكتاب.

وأنا أتفق مع النجم الأزرق الذي ترك تعليقا.قم بإلحاق حرف L بالرقم.

لا، ليس واضحا.

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

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

فقط للإضافة إلى الإجابات الأخرى، وجدت أنه من المفيد في الماضي تحديد الثوابت (public static final long) مثل MILLISECS_DAY أو MILLISECS_HOUR.أكثر قابلية للقراءة ومفيدة.

هناك طريقة أخرى لكتابة هذا

public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = (long) expireTimeInDays * 24 * 60 * 60 * 1000;
}

أو

public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = expireTimeInDays * 24L * 60 * 60 * 1000;
}

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

لم تكن هذه المشكلة واضحة بالنسبة لي في المرة الأولى التي واجهتها فيها.

هناك بعض أدوات التحليل الثابتة (findbugs) التي ستعثر على هذا النوع من الأخطاء.

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

أنا لا أحاول تبرير خطئي، ولكن سيكون من الرائع أن يكون مترجم Java ذكيًا بما يكفي لترقية int إلى وقت طويل قبل الحساب (بمجرد تعيين الحساب لمتغير من النوع الطويل)

بالمناسبة، كنت أعمل مع C/C++ وإذا كان برنامج C، كنت أواجه نفس المشكلة، ولكن منذ بضع سنوات كنت أكثر حرصًا مع هذا النوع من العمليات.

سأولي المزيد من الاهتمام في المرة القادمة (أو أتحول إلى بايثون) ...:د

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