java - هل 1/0 تعبير جافا قانوني؟



divide-by-zero (6)

هل 1/0 بالفعل تعبير جافا قانوني يجب أن يتم تجميعه في أي وقت في أي مكان؟

نعم فعلا.

ماذا تقول JLS عن ذلك؟

لا شيء محدد ... بصرف النظر عن القول بأن القسمة على صفر ستؤدي إلى استثناء وقت التشغيل. ومع ذلك ، يقر JLS أن إمكانية استثناءات وقت التشغيل في التعريف التالي:

"التعبير المستمر للوقت التجميعي هو تعبير يشير إلى قيمة من النوع البدائي أو سلسلة لا تكتمل فجأة وتتكون باستخدام ما يلي فقط: ..."

(التشديد مضاف). لذلك لن يتم تجميع ما يلي:

switch(i) {
    case 1:
    case 1 + 1: 
    case 1 / 0:  // compilation error.
}

إذا كان هذا قانونيًا ، فهل هناك سبب وجيه لذلك؟

سؤال جيد. أفترض أنه طريقة لإلقاء ArithmeticException الرغم من أن هذا ليس سبب معقول. والسبب الأكثر احتمالا لتحديد جافا بهذه الطريقة هو تجنب التعقيد غير الضروري في JLS ومجمعيها للتعامل مع حالة حافة نادرا ما يحدث لدغة الناس.

ولكن هذا كله من قبل. والحقيقة هي أن 1/0 هو رمز Java صالح ، ولا يجب على أي مترجم Java وضع علامة على هذا كخطأ تجميع. (من المنطقي أن يقوم مترجم Java بإصدار تحذير ، شريطة أن يكون هناك مفتاح مترجم لإيقاف تشغيله.)

https://ffff65535.com

ما يلي جمع غرامة في بلدي الكسوف:

final int j = 1/0;
// compiles fine!!!
// throws ArithmeticException: / by zero at run-time

تمنع Java العديد من "الرموز الغبية" من تجميعها في المقام الأول (على سبيل المثال ، "Five" instanceof Number compile!) ، لذا لم يكن هذا حتى قدرا كبيرا من التحذير مفاجئا بالنسبة لي. تتعمق الدويسة عندما تفكر في أن يتم السماح بتعبيرات ثابتة في وقت التجميع:

public class Div0 {
    public static void main(String[] args) {
        final int i = 2+3;
        final int j = 1/0;
        final int k = 9/2;
    }
}

جمع في الكسوف ، والمقتطف أعلاه يولد التالي bytecode ( javap -c Div0 )

Compiled from "Div0.java"
public class Div0 extends java.lang.Object{
public Div0();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_5
   1:   istore_1      // "i = 5;"
   2:   iconst_1
   3:   iconst_0
   4:   idiv
   5:   istore_2      // "j = 1/0;"
   6:   iconst_4
   7:   istore_3      // "k = 4;"
   8:   return

}

كما ترى ، يتم تحسين التخصيصات i و k على أنها ثوابت وقت التجميع ، ولكن يتم تصنيف التقسيم بواسطة 0 (والذي يجب أن يكون قابلاً للاكتشاف في وقت التحويل البرمجي) ببساطة كما هو.

javac 1.6.0_17 يتصرف بشكل أكثر غرابة ، يتم تجميعه بصمت ولكنه يقوم بإسقاط التخصيصات إلى i و k تمامًا من الـ bytecode (ربما لأنهم قرروا أنهم لا يستخدمون في أي مكان) ولكنهم يغادرون 1/0 (لأن إزالته سيسبب دلالات برنامج مختلفة تماما).

وبالتالي فإن الأسئلة هي:

  • هل 1/0 بالفعل تعبير جافا قانوني يجب أن يتم تجميعه في أي وقت في أي مكان؟
    • ماذا تقول JLS عن ذلك؟
  • إذا كان هذا قانونيًا ، فهل هناك سبب وجيه لذلك؟
    • ما الجيد الذي يمكن أن يخدمه هذا؟

إنه قانوني في تجميع وجهة نظر ، لكنه قد يؤدي إلى استثناء إذا تم تنفيذه!

السبب ... يجب أن تسمح البرمجة الجيدة بمرونة ، لذا فإن جميع التعبيرات وكل كود واحد تكتبه هو متغير للمترجم ، وبالتالي في التعبير الرياضي X / Y لا يهتم المحول البرمجي إذا كانت قيمة المتغير Y ( Y == 0 ) أو أي رقم آخر للمترجم هذا متغير ... إذا كان المترجم سيضطر إلى النظر إلى القيم أيضا ، والتي من شأنها أن تعتبر وقت التشغيل ، أليس كذلك.


حسنًا ، إذا نظرت إلى الفصل الدراسي المزدوج ، فسترى ما يلي:

/**
 * A constant holding the positive infinity of type
 * <code>double</code>. It is equal to the value returned by
 * <code>Double.longBitsToDouble(0x7ff0000000000000L)</code>.
 */
public static final double POSITIVE_INFINITY = 1.0 / 0.0;

يتم إجراء نفس الحساب في فئة Float ، باستثناء العوامات بدلاً من المضاعفة. في الأساس ، 1/0 تُرجع رقمًا كبيرًا حقًا ، أكبر من Double.MAX_VALUE.

هذا الكود التالي:

public static void main(String[] args) {
    System.out.println(Double.POSITIVE_INFINITY);
    System.out.println(Double.POSITIVE_INFINITY > Double.MAX_VALUE);
}

المخرجات:

Infinity
true

لاحظ الحالة الخاصة في طباعة Double.POSITIVE_INFINITY . تطبع سلسلة ، على الرغم من أنها تعتبر مزدوجة.

للإجابة على السؤال ، نعم إنه قانوني في Java ، لكن 1/0 يقرر إلى "اللانهاية" ويتم التعامل معه بشكل مختلف عن الزوجين القياسيين (أو العوامات ، أو ما إلى ذلك).

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


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

معرّف الأخطاء 4178182: لا يحدد JLS سلوك 1/0 كتعبير ثابت

التعليمة البرمجية التالية غير قانونية:

class X { static final int i = 1 / 0; }

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

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

Bug ID 4089107: يعامل javac قسمة عدد صحيح بواسطة (ثابت) صفر كخطأ

public class zero {
   public static void main(String[] args) {
      System.out.println(1/0);
   }
}

تشغيل العوائد المذكورة أعلاه:

zero.java:3: Arithmetic exception.
     System.out.println(1/0);
                         ^
1 error

Bug ID 4154563: javac يقبل التقسيم بواسطة تعبيرات ثابتة صفرية في حالة التعبيرات.

تعطل برنامج Java compiler أثناء محاولة تجميع الاختبار التالي. هذا الاختبار أيضاً تعطل كافة إصدارات برنامج التحويل البرمجي 1.2beta4 ولكن الخطأ غير موجود في 12.beta3. على سبيل المثال ، يتبع تشخيص أداة التحويل البرمجي:

public class B {
   public static void main(String argv[]) {
      switch(0){
         case 0/0:
      }
  }
}

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

استنتاج

لذا فإن السؤال حول ما إذا كان يجب أن يكون 1/0 يجب أن 1/0 موضوع مناقشة متنازع عليه ، مع بعض الناس نقلا عن Guy Steele مدعيا أن هذا يجب أن يكون خطأ وقت تجميع ، والبعض الآخر يقول أنه لا ينبغي. يبدو أنه في النهاية قرر أنه ليس خطأ في وقت التجميع ولا ثابت وقت التجميع.


منذ أن أجاب الآخرون بالفعل على شرعية 1/0 ، دعنا ننتقل إلى السؤال الثاني:

  • إذا كان هذا قانونيًا ، فهل هناك سبب وجيه لذلك؟
    • ما الجيد الذي يمكن أن يخدمه هذا؟

الإجابة يمكن أن تكون:

لندف زميل لك. ؛ س)

عندما يغادر الزميل الغرفة مع ترك حاسوبه مفتوحًا ، يتسلل ويضرب 1/0 مكان عميق في مُبدئ ثابت لبعض الصفوف التي يتم استخدامها في وقت مبكر من التطبيق. وبهذه الطريقة سيكتشف قريباً بما فيه الكفاية بعد (أو حتى أثناء) نشر التطبيق من خلال مواجهة " ArithmeticException غير المعتاد ArithmeticException رأسه لفترة من الوقت. باستخدام هذه الطريقة الفاشلة يمكنك التأكد من أنها نكتة غير ضارة نسبيًا.

PS: عملت. ؛ س)


تتطلب Java بشكل صريح قسماً صحيحاً بمقدار صفر لتشغيل ArithmeticException . لا يمكن أن تتم الإحالة إلى j لأن ذلك قد ينتهك المواصفات.





divide-by-zero