Java Ternary operator outputs different result than if else statement - java

I'm writing a program that takes a number and removes the trailing zeros if the number is an integer.
I am using the ternary operator but it doesn't work as expected. But if I write it as a if else statement it works.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double number = scanner.nextDouble();
System.out.println(((int)number == (double)number) ? (int)number : number); // Always outputs a double
if ((int)number == (double)number) { // Outputs correct result
System.out.println((int)number);
}
else {
System.out.println(number);
}
}
}
For example if I input 5 i get
5.0
5
if I input 7.3 I get
7.3
7.3
So it seems that it works for the if else statement but not the ternary operator.

In your if/else statement, you're calling PrintStream.println(int) or PrintStream.println(double) depending on which branch you're taking.
With the conditional operator ?: you're always calling PrintStream.println(double), because that's the type of the ?: expression. When the second and third operands of the ?: operator have different types, the compiler picks the overall type of the expression according to the rules of JLS 15.25 and performs appropriate conversions where necessary.
In this case, the overall type is double, so it's as if you're writing:
double tmp = ((int) number == (double)number) ? (int)number : number;
System.out.println(tmp);

This behavior is totally fine and caused by the type resolution.
The ternary operator itself has a type, that is inferred by the possible outcomes.
Since one outcome has type (int) and the other has type (double), the type of the ternary operator is always double.
The rest of the story should be clear. Printing a double to the console will always result in a decimal point representation.

Related

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.

Difference between if-else and conditional-operator [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.

EXP4J: can't get a good factorial operator working

I have the following factorial operator declared:
Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {
#Override public double apply(double... args) {
final long arg = (long) args[0];
if ((double) arg != args[0]) {
throw new IllegalArgumentException("Operand for factorial has to be an integer");
}
if (arg < 0) {
throw new IllegalArgumentException("The operand of the factorial can not " +
"be " +
"less than zero");
}
double result = 1;
for (int i = 1; i <= arg; i++) {
result *= i;
}
return result;
}
};
So, it works fine when you have simple expressions such as 5! and so on, but things start to break when you try to emulate normal calculator behavior. The following example throws an IllegalArgumentException():
5!+5
Why? I guess it has something to do with 2 adjacent operators or something, since using (5!)+5 works with no issues.
This is devastating for the project I'm working on. Someone has suggested converting the operator into a function but that would take too much time. I've tried changing all the constructor parameters but none of them changed the problem.
I found a workaround that will make the factorial behave as intended. The trick is to add a second variable and make the factorial a pseudo unrary operator, so:
Instead of: 5!+5 I used: 5!(1)+5. This required no refactoring in my program, as the only change made was the way the operator was being inputted.
This way, the parser works wonderfully. You also need to change the actual Operator declaration to this:
Operator factorial = new Operator("!", 2, true, Operator.PRECEDENCE_POWER + 1) {
#Override public double apply(double... args) {
final long arg = (long) args[0];
if ((double) arg != args[0]) {
throw new IllegalArgumentException("Operand for factorial has to be an integer");
}
if (arg < 0) {
throw new IllegalArgumentException("The operand of the factorial can not " +
"be " +
"less than zero");
}
double result = 1;
for (int i = 1; i <= arg; i++) {
result *= i;
}
return result;
}
};
I suppose one should avoid using unary operators altogether when using EXP4J.
It certainly has to do with there being two operators in a row.
Or, alternatively, it has to do with EXP4J being a poor implementation of an expression parser.
Anyway you look at it, there is no simple solution using EXP4J. It is simply not up to the task of handling postfix operators. (If you were prepared to use !5 to mean "5 factorial", there wouldn't be a problem; just make sure that you give it the same precedence as the other unary prefix operators. But the fact that you don't want to use a function suggests that you don't want to change the syntax of ! either.)
The big hint is that the Operator constructor does not have any way to specify whether an operator is prefix or postfix. (! is a postfix operator, because it follows its operand, unlike, say, unary - with is prefix.)
If the expression parser doesn't know, it certainly cannot validate the input, and there's good reason to believe that it can't accurately parse it either. [Note 1] Another hint is that custom operators need to be declared as taking 1 or 2 operands, which means that a custom operator cannot act like - which has both prefix and infix syntax.
The special-case hack for unary + and - operators interacts badly with the hack which doesn't attempt to check whether a unary operator is prefix or postfix. In 5!+5, the + immediately follows an operator, which EXP4J interprets as indicating that the + is a prefix unary +. So it parses that almost as if it were (5!)(+5) except that it does not insert the implicit multiplication that it would insert if you actually wrote (5!)(+5).
So you get the exception, because you can't have two value side-by-side without an operator between them.
Actually fixing this would require quite a bit of rewriting. You might well be better off using a different expression evaluator, or throwing your own together with a standard parser generator.

Why mixing + and cast does not produce an error in "+(int)+(long)-1"?

Why does this print 1?
import java.util.*;
import java.lang.*;
import java.io.*;
class Main
{
public static void main (String[] args) throws java.lang.Exception
{
// your code goes here
System.out.println((byte)+(short)-(int)+(long)-1);
}
}
Can we mix casting and +,- unary operators? I know that we can do casting multiple times, but why doesn't putting + ,- unary operators in between produce an error?
You are not adding nor substracting. Those + and - operators are unary sign operators.
See documentation at The Unary Operators section.
The sequence at stake:
(byte)+(short)-(int)+(long)-1
is evaluated from right to left like this:
the initial value is -1
casting to long (which is still -1)
unary + sign (still -1)
casting to int (still -1)
unary - sign (now the value is 1)
so on (the value remains 1 until the end)
These + and - are unary ones.
More specifically, it is in fact:
System.out.println((byte) (+((short) (-((int) (+((long) -1)))))));
if you remove all casting from your example, because in this case it will do nothing
System.out.println((byte)+(short)-(int)+(long)-1);
will become
System.out.println( + - + -1);
now you can see that just the operators are still there and because minus and minus are plus your result will be 1
basically you can think of it like:
var mylong = +(long)-1; <- -1
var myint = -(int)mylong; <- 1
var myshort = +(short)myint; <- 1
var mybyte = (byte)myshort; <- 1
I know we can do casting multiple times. But putting + ,- unary operators in between doesn't give error?
It is simply a consequence of the consistency of Java's grammar. When you write
+ 1
what you actually wrote is a unary numeric expression which decomposes into two parts:
the unary plus operator: +
a numeric expression, in this case the int literal 1.
Another example of a numeric expression is a cast expression (also unary):
(int) 1
Therefore you can substitute this for the original 1 above:
+ (int) 1
Repeating the same consistent process we can end up with a nested unary expression of arbitrary complexity. To return to your key question:
why?
Because Java would actually need a specific rule against such expressions.

What is the difference between '==' and '='?

I know that one of them is bitwise and the other is logical but I can not figure this out:
Scanner sc = new Scanner(System.in);
System.out.println("Enter ur integer");
int x=sc.nextInt();
if(x=0)//Error...it can not be converted from int to boolean
System.out.println("...");
The error means that x cannot be converted to boolean or the result of x=0 can not be converted to boolean.
== checks for equality.
= is assignment.
What you're doing is:
if( x = Blah ) - in Java this statement is illegal as you can not test the state of an assignment statement. Specifically, Java does not treat assignment as a boolean operation, which is required in an if statement. This is in contrast with C/C++, which DOES allow you to treat assignment as a boolean operation, and can be the result of many hair-pulling bugs.
When you write 'x = 0' you are saying "Store 0 in the variable x". The return value on the whole expression is '0' (it's like this so you can say silly things like x = y = 0).
When you write 'x == 0' it says "Does x equal 0?". The return value on this expression is going to be either 'true' or 'false'.
In Java, you can't just say if(0) because if expects a true/false answer. So putting if(x = 0) is not correct, but if(x == 0) is fine.
== is a comparison operator, and = is assignment.
== is an equality check. if (x == 0) // if x equals 0
= is an assignment. x = 0; // the value of x is now 0
I know the question has been answered, but this still comes up from time to time not as a programmer error but as a typographical error (i.e., the programmer knew what he meant, but failed). It can be hard to see, since the two look so similar.
I've found that a way to help avoid doing this is to put the constant expression on the left-hand-side, like so:
if (0 == x)
...
That way, if I accidentally use only one "=" sign, the compiler will fail with an error about assigning to a constant expression, whether or not the assignment operator is left-associative and whether the if conditional expects a strongly-typed Boolean.
if(x=0)
Here you're assigning the value of 0 to the variable x. The if statement in Java can't evaluate an integer argument as it can in many other languages. In Java, if requires a boolean. Try
if(x == 0)
to do a comparison.
Interpret the error to mean
"The expression
x=0
cannot be converted to Boolean."
Just to clarify about C/C++ - assignment is evaluated to the right operand
if(a = n)
is evaluated to n, so (n = 1) is true (n = 0) is false
One interesting note: Since assignment operator evaluates to the right operand, the following is valid in Java(albeit not pretty):
if (( x = blah ) > 0) ...
Parenthesis are needed because of operator precedence ( '>' binds stronger than '=').
As others have already said, '=' is assignment; '==' is compare.
in your program change
if(x=0)
to
if(x==0)
"==" checks for equality
"=" Is used for assignment.
It is giving you error cause you're assigning value to x in if(), where you're supposed to check for the equality. Try changing it to equality instead of assignment operator.
As others stated, = assigns while == compares.
However, these statements have their own values as well.
The = operator returns the value of its right-hand operand. This is how statements like a = b = c = 5 work: they are parsed as a = (b = (c = 5)), which evaluates to a = (b = 5) and then a = 5.
The == operator returns a boolean that is true if its operands are equal. The if statement runs its body if its argument is true. Thus, if headers like if (5 == 5) translate to if (true). This is why sometimes you see infinite while loops with header while (true); the while loop runs "while" toe argument is true.
If you had a boolean in your if statement, it would give no error and run the code if the value being assigned (or "compared to") was true. This is why it is so important to never mix up the = and == operators, especially when working with booleans.
Hope this helped!!

Categories

Resources