I'm a beginner and there's something that's not making much sense to me. Please could be so kind as to explain where I'm going wrong. I'm sorry if this has been asked before.
Here the presence of the decimal point mean these get evaluated using floating point division.
System.out.println(1/3.0); // this prints: 0.3333333333333333
System.out.println(1.0/3); // this prints: 0.3333333333333333
System.out.println(1.0/3.0); // this prints: 0.3333333333333333
Apparently, this below is an example of "truncating-integer division." It seems a bit weird to me but ok.
System.out.println(1/3); // this prints: 0
Is it ok to say: "in the line below, the (double) cast is evaluated 1st. It effectively says: "treat 1/3 as a double - don't use truncating integer division. instead use floating point division.""
System.out.println((double)1/3); // this prints: 0.3333333333333333
Below, however we get 0.0 - how did that happen?
System.out.println((double)(1/3)); // this prints: 0.0
ok so maybe the extra parentheses mean the (1/3) gets evaluated 1st. It is evaluated using truncating integer division yielding 0. And then the double is applied giving us 0.0 Ok that makes sense
Ok so we maybe can propose a couple of general rules here:
Rule 1: (double) expression means apply the (double) cast first then evaluate the expression.
Rule 2: (double) (expression) means evaluate the expression then apply the cast. Great!
So in the next line below we have: (int) expression, so I guess we can apply rule 1). The (int) cast is evaluated 1st. It effectively says: "treat 1.0/3 as a int - don't use as much memory as you would with a double. Don't use floating point division, instead apply truncating integer division."" So we have 0 right? No.
System.out.println((int)1/3.0); // this prints: 0.3333333333333333
Ok so we have 0.33333 so the (int) cast is not evaluated 1st. It is as if it wasn't there. Let's propose a 3rd rule:
Rule 3: (int) expression means ignore the (int) cast altogether just evaluate the expression like the (int) isn't even there.
Ok applying rule 3 to the line below, we have (int) but we are just going to ignore it. 1.0/3.0 is evaluated with floating point division and we get 0.3333333. Success!
System.out.println((int)1.0/3.0); // this prints: 0.3333333333333333
And in the last line below, again we have (int) but we are just going to ignore it. (1.0/3) is evaluated using floating point division yielding 0.3333333333 right? No.
System.out.println((int)(1.0/3)); // this prints: 0
Ok now I'm confused. Please could you help me get my head round this?
Java's language parsing rules are basically implying a lot of parentheses everywhere in your code based on its rules of order of operations. Understanding where Java thinks the parentheses are will help you understand this behavior. When you say:
(double) 1 / 3.0
... this is equivalent to saying:
((double) 1) / 3.0
This means that 1 gets converted to a double, and 3.0 is automatically a double, so you'll end up doing a floating point division rather than integer division. The same thing would happen with (double) 1 / 3
, because the divisor is a double
, so even though the dividend is an int
, the system realizes that you want to do floating-point division. The compiler doesn't want you to lose precision unless you specifically ask to, so any time either the dividend or the divisor is a double
, it will do double
division.
On the other hand:
(int) 1 / 3.0
... is the same as saying:
((int) 1) / 3.0
In this case, you're telling the compiler what it already knew: that the divisor (1) is an int
. Then you're asking it to divide by a double
value (3.0). Since the dividend is a double
, it will perform double
division.
As you noted, 1/3
will produce zero, because that's how integer division works and both numbers are integers. The same will happen with 1/(int)3.0
because you're telling the compiler to make the 3.0
into an int
before the division will occur (as in 1/((int)3.0)
).
The final point to keep in mind is that (int) 0.33
will also get converted to 0
, because an integer can't hold decimal values in it. So when you say (int)(1.0/3)
, you're doing double
division, but then you're converting the double into an int
afterwards, producing 0
.
This is specified in the Java Language Specification - Numeric Promotions
If any of the operands is of a reference type, unboxing conversion (§5.1.8) is performed.
Then:
If either operand is of type double, the other is converted to double.
Otherwise, if either operand is of type float, the other is converted to float.
Otherwise, if either operand is of type long, the other is converted to long.
Otherwise, both operands are converted to type int.
The concept is called widening conversion:
5.1.2 Widening Primitive Conversion
The following 19 specific conversions on primitive types are called the widening primitive conversions:
- byte to short, int, long, float, or double
- short to int, long, float, or double
- char to int, long, float, or double
- int to long, float, or double
- long to float or double float to double
The reason why
(int) (1.0/3) == 0
is because you cast the result of 1.0/3 (0.33333... by the above rules) explicitly to an integer, which is equivalent to "floor(result)" (rounding down), so the result is 0.
Cast has a higher precedence than /, that's why the other operations were misleading to your understanding:
(double) 1 / 3
will explicitly cast 1 to a double value 1.0 first, so again the result by the above rules is equal to 0.3333....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With