views:

567

answers:

7

The following compiles fine in my Eclipse:

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

Java prevents many "dumb code" from even compiling in the first place (e.g. "Five" instanceof Number doesn't compile!), so the fact this didn't even generate as much as a warning was very surprising to me. The intrigue deepens when you consider the fact that constant expressions are allowed to be optimized at compile time:

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

Compiled in Eclipse, the above snippet generates the following 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

}

As you can see, the i and k assignments are optimized as compile-time constants, but the division by 0 (which must've been detectable at compile-time) is simply compiled as is.

javac 1.6.0_17 behaves even more strangely, compiling silently but excising the assignments to i and k completely out of the bytecode (probably because it determined that they're not used anywhere) but leaving the 1/0 intact (since removing it would cause an entirely different program semantics).

So the questions are:

  • Is 1/0 actually a legal Java expression that should compile anytime anywhere?
    • What does JLS say about it?
  • If this is legal, is there a good reason for it?
    • What good could this possibly serve?
A: 

It's legal because no where is it a given that the compiler is supposed to fold constant expressions at compile time.

A "smart" compiler might compile:

a = 1 + 2

as

a = 3

But there's nothing that says the compiler HAS to do that. Other than that, 1/0 is a legal expression just like:

int a;
int b;

a = a/b;

is a legal expression.

At RUNTIME it throws an exception, but that's a runtime error for a reason.

Will Hartung
`int a; int b; a = a/b;` throws a compile time exception by javac.
Bart Kiers
@Bart K. - there's no such thing as an "exception that is thrown at compile time". The compiler will indeed give you an error. Exceptions are only thrown at runtime.
Jesper
@Jesper, yes, you're right.
Bart Kiers
*sigh* The expression is legal, the compiler error is due to the uninitialized variables. Surprised I wasn't voted down for missing the semi-colon on the first expression.
Will Hartung
@Will, I didn't say anything about the expression being (il)legal. You said *"At RUNTIME it throws an exception"* which is not true: `int a; int b; a = a/b;` won't reach runtime.
Bart Kiers
@Bart but you are being pedantic expecting code written simply to promote the narrative is supposed to be legal and perfect code. The intent of the code is clear, even if it is imperfect Java because the a and b aren't initialized. I typed them to ensure someone wouldn't chime it "what if b is a String" or some other nonsense.
Will Hartung
@Will, no, I'm not being pedantic (at least, it is not my intention). Since you specifically said it would throw a runtime exception, I merely pointed out that this is not true. And obviously your intent was not clear (to me) since I thought that you meant that the "not initialized" error would be thrown at runtime, which was, looking at your comments, not your intent.
Bart Kiers
+19  A: 

Is 1/0 actually a legal Java expression that should compile anytime anywhere?

Yes.

What does JLS say about it?

Nothing specific ... apart from saying that division by zero will result in a runtime exception. However, the JLS acknowledges that possibility of runtime exceptions in the following definition:

"A compile-time constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following: ..."

(Emphasis added.) So the following would NOT compile:

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

If this is legal, is there a good reason for it?

Good question. I suppose that it is a way to throw ArithmeticException thought that is hardly a plausible reason. A more likely reason for specifying Java this way is to avoid unnecessary complexity in the JLS and compilers to deal with an edge case that is rarely going to bite people.

But this is all by the by. The fact is that 1/0 is valid Java code, and no Java compiler should ever flag this as a compilation error. (It would be reasonable for a Java compiler to issue a warning, provided that there was a compiler switch to turn it off.)

Stephen C
+1, nice example with `switch`.
polygenelubricants
It reasonable to not have a warning, as dividing by a compile time constant which evaluates to zero isn't a common enough cause of errors to waste development time detecting.
Pete Kirkham
@Pete - I can accept that, though there are precedents for this kind of warning. For example the Eclipse Java compiler will emit a warning if it figures out that a statement will always throw an NPE.
Stephen C
Considering the number of times I've hit a NPE vs an ArithmeticException, that's quite reasonable.
Pete Kirkham
+2  A: 

Java explicitly requires integer division by zero to trigger an ArithmeticException. The assignment to j can't be elided because that would violate the spec.

Marcelo Cantos
If it compiles, certainly it can't be elided; this much is known and is acknowledged. The question is whether it should even compile in the first place.
polygenelubricants
A: 

It is legal in compiling point of view, but it would throw an exception if executed!

the reason... well programming must allow flexibility therefore all the expressions and every single code you type is a variable for the compiler, thus in the mathematical expression X/Y the compiler does not care if the Y variable value is (Y==0) or any other number for the compiler this is a variable... if the compiler would have to look at values also, that would be considered runtime, wouldn't it.

TacB0sS
I don't think that checking for division by zero would increase compile time considerably.
Helper Method
It would if the "zero" was a variable. Then Java would have to perform every calculation involving that variable preceding the division statement to determine whether or not it is zero at that point, and in many instances this is impossible (e.g. variable loaded from external source). If it WASN'T a variable, it'd mean you actually coded: a = b/0;and that would be a pretty amateurish mistake one could surely only make while under the influence :P
Daniel
never said it would increase compiling time. what I was trying to say was that the compiler does not evaluate the expression on compilation, but it does determine the type of the variable (int/float/...) in the division and its value and saves the type and value in the class file as byte code, and it does not evaluate the expression. Because it would be endless and pointless to evaluate all the expressions possible.
TacB0sS
A: 

Why bother catching this at compile-time, when you're going to need a run-time variant anyway?

For example, if you were loading and parsing "0" from a text file, then tried to divide by it, Java would have no idea what you were doing at compile-time because it doesn't know the contents of that external file.

Also if you were to set any variable to 0 and divide by the variable, Java would have to keep track of every possible value of every variable at every point in the script in order to catch a divide by 0 at compile time.

Might as well keep things consistent and make it a runtime-only exception.

Daniel
Why bother catching `String s = (String) Integer.valueOf(0); // doesn't compile!` at compile-time if it's going to throw `ClassCastException` at run-time anyway?
polygenelubricants
Because Integer.valueOf(0) is a type issue (integer argument when it was expecting a string) and type issues are detected at compile-time thanks to Java's strict typing.Division by zero is a calculation issue, and Java does not perform calculations at compile-time because in many situations it's impossible (e.g. calculations on a variable loaded from a DB/external file). Therefore all calculation issues are caught at run-time.
Daniel
+2  A: 

Well, if you look into the Double class, you will see the following:

/**
 * 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;

The same calculation is made in the Float class, except with floats instead of doubles. Basically, 1/0 returns a really, really big number, larger than Double.MAX_VALUE.

This following code:

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

Outputs:

Infinity
true

Note the special case in printing out Double.POSITIVE_INFINITY. It prints out a string, though it's regarded as a double.

To answer the question, yes it is legal in Java, but 1/0 resolves to "infinity" and is treated differently from standard Doubles (or floats, or so on and so forth).

I should note that I do not have the slightest clue how or why it was implemented this way. When I see the above output, it all seems like black magic to me.

Corey
Floating point types (`double` and `float`) work differently from integers. The FP types have a way to represent infinity, which the integer types do not have. The original question was about integers, so a discussion about FP types is not really relevant.
Jesper
The question is asking if division by zero "should compile anytime anywhere?". The examples provided do use integers, but it is never explicitly asked if integers in particular can hold a value divided by zero, only if it's legal in the language. The answer is to that, then, is yes.
Corey
The question explicitly asks about the expression 1/0, not the expression 1.0/0.0, so I still agree with Jesper's comment. Imo, it was quite obvious that this was a discussion whether if, and if so why, 1/0 should be a compile time error as opposed to a runtime exception, neither of which is appropriate for floating point types.
Alderath
+6  A: 

I did some digging into the Bug Database, and discovered some interesting information.

Bug ID 4178182: JLS doesnt specify behavior for 1/0 as a constant expression

The following code is illegal:

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

The value of this compile-time constant is undefined, therefore this has to be a compile-time error. Guy Steele confirmed about 18 months ago that this was indeed the intended behaviour.

A compile-time constant has to have its value available statically (that's what makes it a compile-time constant ;-) For example, the value of other constants whose values are determined by a constant that contains a division by zero are undefined. This affects the semantics of switch statements, definite assigment and unassignment, etc.

Bug ID 4089107: javac treats integer division by (constant) zero as an error

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

Running the above yields:

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

Bug ID 4154563: javac accepts division by zero constant expressions in case expressions.

Java compiler crashes while trying to compile next test. This test also crashes all 1.2beta4 compiler versions, but bug is absent in 12.beta3. An example and compiler diagnostics follow:

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

Evaluation: The compiler used to report all attempts to divide by the constant zero as compile-time errors. This was fixed in beta3 so that code would be generated for division by constant zero. Unfortunately this bug was introduced. The compiler should handle a division by zero in a case expression gracefully.

Conclusion

So the question of whether or not 1/0 should compile was a contested topic of discussion, with some people quoting Guy Steele claiming that this should be a compile time error, and others saying that it shouldn't. It seems that ultimately it's decided that it's neither a compile-time error nor a compile-time constant.

polygenelubricants