Java - ternary operator weird behaviour - java

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?

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.

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

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.

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

Related

Is there any difference between `x > 0` and `x > 0.0D` in Java?

The following code is from the Math.java of java standard library:
public static double abs(double a) {
return (a <= 0.0D) ? 0.0D - a : a;
}
I have questions that:
Is the code equal to return (a <= 0) ? 0.0D - a : a;
Furthermore, is the code euqal to return (a <= 0) ? - a : a;
Thanks for any help!
a <= 0 when a is double converts 0 to double (due to binary numeric promotion), which means it's equivalent to a <= 0.0D.
a <= 0.0D might be more efficient, since it saves the conversion, but I wouldn't be surprised if the compiler converts a <= 0 to a <= 0.0D in order to save that type conversion.
Relevant JLS quotes:
15.20.1. Numerical Comparison Operators <, <=, >, and >=
Binary numeric promotion is performed on the operands (§5.6.2).
5.6.2. Binary Numeric Promotion
If either operand is of type double, the other is converted to double.
As for your second question, the code that uses unary negation operator is not equivalent to the code of the abs method, since:
For floating-point values, negation is not the same as subtraction from zero, because if x is +0.0, then 0.0-x is +0.0, but -x is -0.0. Unary minus merely inverts the sign of a floating-point number.
(15.15.4. Unary Minus Operator -)
BTW, the case of positive zero or negative zero is mentioned as a special case in the Javadoc of the abs method:
Special cases:
• If the argument is positive zero or negative zero, the result is positive zero.
• If the argument is infinite, the result is positive infinity.
• If the argument is NaN, the result is NaN.
The first two special cases are the cases in which -a differs from 0.0D - a.

Different result between normal conditional and ternary operation [duplicate]

I'm trying to write a method which gets a double, verifies if the number has something after the dot and if it does—returns a double, if doesn't—returns an int.
public class Solution {
public static void main(String[] args) {
double d = 3.000000000;
System.out.println(convert1(d));
System.out.println(convert2(d));
}
static Object convert1(double d) {
if(d % 1 == 0)
return (int) d;
else
return d;
}
static Object convert2(double d) {
return ((d%1) == 0) ? ((int) (d)) : d;
}
}
Output:
3
3.0
So, everything I want happens in method convert1(), but doesn't happen in method convert2(). It seems as these methods must do the same work. But what I have done wrong?
You're seeing an effect similar to the one in this question.
Slightly different rules govern the way Java handles types with the ternary operator than with an if statement.
Specifically, the standard says:
The type of a conditional expression is determined as follows:
...
Otherwise, if the second and third operands have types that are
convertible (§5.1.8) to numeric types, then there are several cases:
...
Otherwise, binary numeric promotion (§5.6.2) is applied to the
operand types, and the type of the conditional expression is the
promoted type of the second and third operands.
Flipping to that page of the standard, we see:
If either operand is of type double, the other is converted to double.
which is what's happening here, followed by autoboxing to a Double. It appears that no such conversion happens with the if statement, explaining the difference.
More broadly --- this isn't a very good idea. I don't think it's good design to return one of an int or a double depending on the value -- if you want to round something off, use Math.floor, and if you don't want decimals printed, use printf.
EDIT: I don't think it's a good idea to do hacky things to circumvent the regular numeric conversion system. Here's an idea that gives you a String directly, which appears to be what you want:
static String convert3(double d) {
return ((d % 1 == 0) ? Integer.toString((int)d) : Double.toString(d));
}
As the other answers have stated, this behavior is because both possible results of a ternary expression must have the same type.
Therefore, all you have to do to make your ternary version work the same way as convert1() is to cast the int to an Object:
static Object convert2(double d) {
return ((d % 1) == 0) ? ((Object) (int) (d)) : d;
}
The ternary operator requires both result values be the same type, so the int undergoes an automatic (safe) widening cast to double.
The ternary is not exactly the same as its if "equivalent".
To solve the problem with numbers after the dot:
public Object convert(double number){
double whole = Math.floor(number);
if(Math.abs(whole - number) < DELTA){
return (int) number;
}
return number;
}
The DELTA is sufficiently small constant, to solve the problem with integers encoded in floating point format.
I have written the code from memory, but I think the idea behind it is clear.

Java ternary operator datatype conversion based on the first value?

I came across this thing which I havent noticed before.
Here is a normal expression
int a = 5;
System.out.println(((a < 5) ? 0 : 9));
so this just prints 9 as an int. Now If I put the first value String instead of an int 0
int a = 5;
System.out.println(((a < 5) ? "asd" : 9));
Here the value 9 is printed as a string and not as an int. To confirm this just try to add this with another integer
int a = 5;
System.out.println((((a < 5) ? 0 : 9) + 4) );
Now this results in 13 but if you change the first value to string instead of an int 0 it gives a compile error
"The operator + is undefined for the argument type(s) Object&Serializable&Comparable<?>, int".
I am confused with this compile error. What is actually behind this ? Thanks for explanation
The type of
(a < 5) ? "asd" : 9
is
Object&Serializable&Comparable<?>
You can see this in the compiler error later in the question. The int 9 is boxed to Integer and then the common type between that and String is found. So you are actually calling the println(Object) overload not println(String) or println(int). println(Object) calls toString on its argument.
If you try to apply + to a Object&Serializable&Comparable<?> then neither string concatenation or arithmetic is applicable, hence the compiler error.
The type of the expression (a < 5) ? "asd" : 9 depends on the types of the 2nd and 3rd operands - "asd" and 9. The only common type to those two operands is Object (if the int is boxed to Integer).
Therefore the type of the expression is Object, Java has no + operator that accepts Object and int as operands. Hence the compilation error.
In
System.out.println((((a < 5) ? 0 : 9) + 4));
the type of the ternary conditional expression is int, and int and int are acceptable operands of the + operator.

What declaration turns the below loop into infinite loop?

Place your declaration for i at line 3 so that the loop becomes an infinite loop.
public class Puzzel3 {
public static void main(String[] args) {
// Line 3
while (i == i + 1) {
System.out.println(i);
}
System.out.println("done");
}
}
Math says, that Infinity + 1 == Infinity, so
// The declaration required
double i = Double.POSITIVE_INFINITY;
// It's infinite loop now...
while (i == i + 1) {
System.out.println(i);
}
System.out.println("done");
double i=1/0.0;
It will turn the loop in infinite
The while loop is infinite if the loop condition remains true. Since the expression only depends on i, and i is not assigned in the loop body, that is equivalent to the loop condition being true on first evaluation.
Therefore, the question is for which values of which types the expression i == i + 1 is true.
Java has the following types:
reference types: do not support the + operator, except for strings, which get longer by concatentating "1", and therefore can not remain identical.
primitive types:
boolean: does not support +
integral types: adding 1 is guaranteed to change the value, even in case of overflow
floating point types: a float of floating point type is either:
positive 0: 0- + 1 is 1 and therefore != 0
negative 0: 0+ + 1 is 1 and therefore != 0
NaN: NaN + 1 is NaN, but NaN != NaN
positive infinity: inf+ + 1 is inf+, and therefore == inf+
negative infinity: inf- + 1 is inf-, and therefore == inf-
normal: c + 1 is not an accurate computation. Roughly speaking, 1 is added to c, and the nearest float (or double) to that value is taken as the result. Whether that float (or double) is distinct from the initial value depends on the density of floating point values around c. Internally, a floating point type is represented by a sign bit, and two fixed-with integers m and e, where the value of the float is given by s * m * 2^e.
Adding 1 will unlikely change e (and if it does, the result is distinct anyway). Otherwise:
if e <= 0, adding 1 will change m
if e == 1, adding 1 might change m, depending on the rounding mode
if e > 1, adding 1 will not change m, and therefore c + 1 == c. Now, for which values will this occur?
For float, m < 2^24. Therefore, e > 1 if c >= 2^25 or c <= - (2^25)
For double, m < 2^53. Therefore, e > 1 if c >= 2^54 or c <= -(2^54)
Those ought to be all cases :-)

ternary operator not working

Netbeans is saying that my ternary operator isn't a statement. How come?
int direction;
direction = (Math.random() < 0.5) ? 0 : 1; // direction is either L or R (0 or 1)
direction == 0 ? System.out.print('L') : System.out.print('R');
I tried it's if/then/else counterpart and it works fine:
int direction;
direction = (Math.random() < 0.5) ? 0 : 1; // direction is either L or R (0 or 1)
if(direction == 0){
System.out.print('L');
} else {
System.out.print('R');
}
The statements in the ternary operator need to be non-void. They need to return something.
System.out.println(direction == 0 ? 'L' : 'R');
A ternary operator is intended to evaluate one of two expressions, not to execute one of two statements. (Invoking a function can be an expression if the function is declared to return a value; however, System.out is a PrintStream and PrintStream.print is a void function.) You can either stick with the if...else structure for what you're trying to do or you can do this:
System.out.print(direction == 0 ? 'L' : 'R');
NOTE: The comment by #iamcreasy points out a bit of imprecision in how I phrased things above. An expression can evaluate to nothing, so what I should have said was that a ternary operator evaluates one of two non-void expressions. According to the Java Language Specification §15.25:
It is a compile-time error for either the second or the third operand
expression to be an invocation of a void method.
From the JLS section 15.25. Conditional Operator ?:
It is a compile-time error for either the second or the third operand expression to be an invocation of a void method.
both the second and third operand expression here:
direction == 0 ? System.out.print('L') : System.out.print('R');
are void so this is a not a valid use of a ternary expression. You could either stick to the if else or use something similar to this alternative:
System.out.print( direction == 0 ? 'L' : 'R' );
Also the logic here is not correct:
direction = (int)(Math.random() * 1);
direction will always evaluate to 0 since Math.random() generates numbers in the range [0.0,1.0) which means it does not include 1.0 and casting a double to int will just drop the decimals. Using nextInt(2) is a good alternative.

Categories

Resources