24

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?

4
  • 3
    It chooses d.intValue() in both cases, it just performs conversions to make sure the second and third argument have the same type. Commented Mar 27, 2016 at 8:41
  • Could probably considered as a duplicate of stackoverflow.com/questions/8002603/… (and all its duplicates) ... Commented Mar 27, 2016 at 12:55
  • @Marco13, i was not aware that casting was the problem while posting the question. maybe it's a duplicate by hindsight.. Commented Mar 27, 2016 at 15:26
  • You are trying to remove the fractional part from a double. Commented Mar 27, 2016 at 21:48

4 Answers 4

35

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.

Sign up to request clarification or add additional context in comments.

2 Comments

But why is it being cast to Object and not String when both can be assigned to a String?
@Daniel Actually, Integer cannot be assigned to a String, it has to be converted to a String (for example, by using Integer's toString) first. It's only converted to a String in your example in order to be printed by println.
11

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));

1 Comment

Thanks, in my case (number represents csat) the max possible value is 5 so life's thankfully simpler than that..
3

It chooses correctly. Then it wraps it in double. These are 3 key points:

  1. 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.

  2. 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.

  3. 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.

Comments

1

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); 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.