Reputation: 6491
I was trying to remove the fractional
part from a double
in case it is whole using:
(d % 1) == 0 ? d.intValue() : d
And encountered the following behavior which i don't understand:
public static void main(String[] args) {
Double d = 5D;
System.out.println((d % 1) == 0); // true
System.out.println((d % 1) == 0 ? d.intValue() : "not whole"); // 5
System.out.println((d % 1) == 0 ? d.intValue() : d); // 5.0
}
As you can see on the third line, the operator chooses the else
value - 5.0
even though the condition (d % 1) == 0
is met.
What's going on here?
Upvotes: 24
Views: 2133
Reputation: 10523
In your case the second and third arguments of the ternery operator are types "int" and "Double". Java must convert these values to the same type so they can be returned from the ternary operator. The rules for doing this are given in the Java language specification. https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25
In your case these rules result in the conversion of both parameters to type "double" (the "Double" is unboxed, the int is value-converted).
The fix is to cast the arguments to the ternary operator so that they are of the same type (there may be more brackets in the below than strictly needed, i'm a bit rusty on java operator precedence rules).
System.out.println((d % 1) == 0 ? ((Number)(d.intValue())) : (Number)d);
Upvotes: 1
Reputation: 442
It chooses correctly. Then it wraps it in double. These are 3 key points:
If the second and third operands have the same type, that is the type of the conditional expression. In other words, you can avoid the whole mess by steering clear of mixed-type computation.
If one of the operands is of type T where T is byte , short , or char and the other operand is a constant expression of type int whose value is representable in type T, the type of the conditional expression is T.
Otherwise, binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
Upvotes: 3
Reputation: 394146
The return type of the ternary conditional operator must be such that both the 2nd and 3rd operands can be assigned to it.
Therefore, in your second case, the return type of the operator is Object
(since both d.intValue()
and "not whole"
must be assignable to it) while in the third case it is Double
(since both d.intValue()
and d
must be assignable to it).
Printing an Object
whose runtime type is Integer
gives you 5
while printing a Double
gives you 5.0
.
Upvotes: 35
Reputation: 533880
The type of an expression a ? b : c
is always the same as c
or the closest common parent of b
and c
.
System.out.println((d % 1) == 0 ? d.intValue() : "not whole"); // Comparable a parent of Integer and String
System.out.println((d % 1) == 0 ? d.intValue() : d); // Double is a widened int
BTW d % 1
will only check it is a whole not, not that it's small enough to fit in anint
value. A safer check is to see if the value is the same when cast to an int
or long
double d = 5.0;
if ((long) d == d)
System.out.println((long) d);
else
System.out.println(d);
or you can prevent it widening the long
back to a double with
double d = 5.0;
System.out.println((long) d == d ? Long.toString((long) d) : Double.toString(d));
Upvotes: 11