سلوك متفاوت لفقدان الدقة المحتمل
-
01-10-2019 - |
سؤال
في جاوة عندما تفعل
int b = 0;
b = b + 1.0;
تحصل على خسارة محتملة لخطأ الدقة. ولكن لماذا إذا قمت بذلك
int b = 0;
b += 1.0;
لا يوجد أي خطأ؟
المحلول
هذا بسبب b += 1.0;
يعادل b = (int) ((b) + (1.0));
. ال تضييق التحويل البدائي (JLS 5.1.3) مخفي في عملية المهمة المركب.
JLS 15.26.2 مشغلي الواجبات المركبة (JLS Third Edition):
تعبير تخصيص مركب للنموذج E1 OP = E2 يعادل E1 = (T) ((E1) OP (E2)), ، أين ر هو نوع E1, ، ماعادا هذا E1 يتم تقييمه مرة واحدة فقط.
على سبيل المثال ، الكود التالي صحيح:
short x = 3; x += 4.6;
ونتائج في
x
وجود القيمة7
لأنه يعادل:short x = 3; x = (short)(x + 4.6);
وهذا ما يفسر أيضًا سبب تجميع الكود التالي:
byte b = 1;
int x = 5;
b += x; // compiles fine!
لكن هذا لا:
byte b = 1;
int x = 5;
b = b + x; // DOESN'T COMPILE!
تحتاج إلى الإلقاء بشكل صريح في هذه الحالة:
byte b = 1;
int x = 5;
b = (byte) (b + x); // now it compiles fine!
تجدر الإشارة إلى أن المصبوب الضمني في المهام المركبة هو موضوع اللغز 9: Tweedledum من الكتاب الرائع Java Puzzlers. إليك بعض المقتطفات من الكتاب (تم تحريره قليلاً للإيجاز):
يعتقد العديد من المبرمجين ذلك
x += i;
هو مجرد اختصار لx = x + i;
. هذا ليس صحيحًا تمامًا: إذا كان نوع النتيجة أوسع من المتغير ، فإن مشغل المهمة المركب يؤدي تحويلًا بدائيًا صامتًا.لتجنب المفاجآت غير السارة ، لا تستخدم عوامل التعيينات المركب على متغيرات النوع
byte
,short
, ، أوchar
. عند استخدام مشغلي التعيين المركب على متغيرات النوعint
, ، تأكد من أن التعبير على الجانب الأيمن ليس من النوعlong
,float
, ، أوdouble
. عند استخدام مشغلي التعيين المركب على متغيرات النوعfloat
, ، تأكد من أن التعبير على الجانب الأيمن ليس من النوعdouble
. هذه القواعد كافية لمنع المترجم من توليد قوالب ضيقة خطيرة.بالنسبة لمصممي اللغة ، من المحتمل أن يكون من الخطأ لمشغلي المهام المركبة إنشاء قوالب غير مرئية ؛ يجب أن تكون المهام المركبة حيث يكون للمتغير نوع أضيق من نتيجة الحساب غير قانونية.
الفقرة الأخيرة تجدر الإشارة إلى: C# أكثر صرامة في هذا الصدد (انظر C# مواصفات اللغة 7.13.2 تعيين المركب).