Difference between if-else and conditional-operator [duplicate] - java

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.

Related

Recursion sum with generics [duplicate]

I have two Numbers. Eg:
Number a = 2;
Number b = 3;
//Following is an error:
Number c = a + b;
Why arithmetic operations are not supported on Numbers? Anyway how would I add these two numbers in java? (Of course I'm getting them from somewhere and I don't know if they are Integer or float etc).
You say you don't know if your numbers are integer or float... when you use the Number class, the compiler also doesn't know if your numbers are integers, floats or some other thing. As a result, the basic math operators like + and - don't work; the computer wouldn't know how to handle the values.
START EDIT
Based on the discussion, I thought an example might help. Computers store floating point numbers as two parts, a coefficient and an exponent. So, in a theoretical system, 001110 might be broken up as 0011 10, or 32 = 9. But positive integers store numbers as binary, so 001110 could also mean 2 + 4 + 8 = 14. When you use the class Number, you're telling the computer you don't know if the number is a float or an int or what, so it knows it has 001110 but it doesn't know if that means 9 or 14 or some other value.
END EDIT
What you can do is make a little assumption and convert to one of the types to do the math. So you could have
Number c = a.intValue() + b.intValue();
which you might as well turn into
Integer c = a.intValue() + b.intValue();
if you're willing to suffer some rounding error, or
Float c = a.floatValue() + b.floatValue();
if you suspect that you're not dealing with integers and are okay with possible minor precision issues. Or, if you'd rather take a small performance blow instead of that error,
BigDecimal c = new BigDecimal(a.floatValue()).add(new BigDecimal(b.floatValue()));
It would also work to make a method to handle the adding for you. Now I do not know the performance impact this will cause but I assume it will be less than using BigDecimal.
public static Number addNumbers(Number a, Number b) {
if(a instanceof Double || b instanceof Double) {
return a.doubleValue() + b.doubleValue();
} else if(a instanceof Float || b instanceof Float) {
return a.floatValue() + b.floatValue();
} else if(a instanceof Long || b instanceof Long) {
return a.longValue() + b.longValue();
} else {
return a.intValue() + b.intValue();
}
}
The only way to correctly add any two types of java.lang.Number is:
Number a = 2f; // Foat
Number b = 3d; // Double
Number c = new BigDecimal( a.toString() ).add( new BigDecimal( b.toString() ) );
This works even for two arguments with a different number-type. It will (should?) not produce any sideeffects like overflows or loosing precision, as far as the toString() of the number-type does not reduce precision.
java.lang.Number is just the superclass of all wrapper classes of primitive types (see java doc). Use the appropriate primitive type (double, int, etc.) for your purpose, or the respective wrapper class (Double, Integer, etc.).
Consider this:
Number a = 1.5; // Actually Java creates a double and boxes it into a Double object
Number b = 1; // Same here for int -> Integer boxed
// What should the result be? If Number would do implicit casts,
// it would behave different from what Java usually does.
Number c = a + b;
// Now that works, and you know at first glance what that code does.
// Nice explicit casts like you usually use in Java.
// The result is of course again a double that is boxed into a Double object
Number d = a.doubleValue() + (double)b.intValue();
Use the following:
Number c = a.intValue() + b.intValue(); // Number is an object and not a primitive data type.
Or:
int a = 2;
int b = 3;
int c = 2 + 3;
I think there are 2 sides to your question.
Why is operator+ not supported on Number?
Because the Java language spec. does not specify this, and there is no operator overloading. There is also not a compile-time natural way to cast the Number to some fundamental type, and there is no natural add to define for some type of operations.
Why are basic arithmic operations not supported on Number?
(Copied from my comment:)
Not all subclasses can implement this in a way you would expect. Especially with the Atomic types it's hard to define a usefull contract for e.g. add.
Also, a method add would be trouble if you try to add a Long to a Short.
If you know the Type of one number but not the other it is possible to do something like
public Double add(Double value, Number increment) {
return value + Double.parseDouble(increment.toString());
}
But it can be messy, so be aware of potential loss of accuracy and NumberFormatExceptions
Number is an abstract class which you cannot make an instance of. Provided you have a correct instance of it, you can get number.longValue() or number.intValue() and add them.
First of all, you should be aware that Number is an abstract class. What happens here is that when you create your 2 and 3, they are interpreted as primitives and a subtype is created (I think an Integer) in that case. Because an Integer is a subtype of Number, you can assign the newly created Integer into a Number reference.
However, a number is just an abstraction. It could be integer, it could be floating point, etc., so the semantics of math operations would be ambiguous.
Number does not provide the classic map operations for two reasons:
First, member methods in Java cannot be operators. It's not C++. At best, they could provide an add()
Second, figuring out what type of operation to do when you have two inputs (e.g., a division of a float by an int) is quite tricky.
So instead, it is your responsibility to make the conversion back to the specific primitive type you are interested in it and apply the mathematical operators.
The best answer would be to make util with double dispatch drilling down to most known types (take a look at Smalltalk addtition implementation)

Java Ternary operator outputs different result than if else statement

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.

Why System.out.print(ternary operator) print float in output?

I was going through some java interview questions MCQ where I found this code snippet, of which I didn't understand the output, though its only a 2 line code.
int a = 8;
System.out.println(((a<8)? 9.9 : (int)9));
Output is 9.0
I didn't understand why it is not 9 ?
Ternary operator has return type that is defined before the calculation of the value.
So, if the operator can return both float and int, then the both values are upcasted to the float.
Your answer is casted in this way:
(int)9 -> (int)9 -> (float)9.
Other situation: If you add float and int, you get float
int a = 2;
float b = 4.3f;
float c = a + b;
Because you are not casting all of them . you are just casting second result to int.
But don't forget first result is float so all of structure must be same type.
You need to cast all of them as same type like int or float.
int a = 8;
System.out.println(""+ (int)( (a<8)? 9.9 : 9));
output :
9
The return type of the ternary operator is determined according to quite complicated rules:
Java Language Specification. Specifically, in your case:
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.
So in your case you get return type double.

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.

Cast from float to double

Can someone please explain me why this code doesn‘t compile:
boolean r = (boolean) 0;
Why does this one compile?
double z = (float) 2.0_0+0___2;
I don‘t understand the Alphabet in which the numbers after float are written.
The first one doesn't compile because you simply can't cast a number to a boolean. A boolean is true or false.
The second one just uses underscores, which can be used to separate numbers like 2_000_000 for improved readability. In this case they're used to decrease readability, as is the cast to float (a double cast to float and assigned to double doesn't do anything in this particular case).
The latter case seems to be designed for confusion, as there are several pitfalls. If we remove the unnecessary underscores we get 2.00+02 which adds a literal double with an octal 02. This is still basically just 2+2, but if the octal value were 0___10 you'd get a result of z = 10. Then you have the cast to float which could affect the final result, as 64 bits are forced to 32 bits and then back to 64 bits. This could make the end result less precise than without the cast.
In some languages, like PHP or Javascript 0 is falsy, that is, not false, but evaluated as a boolean value, it will be false. In C, 0 is false. These are possible reasons for your expectation. However, in Java you cannot convert a number to a boolean. If you want to have a truey-ish evaluation, you can implement helper methods, like:
public class LooselyTyped {
public boolean toBoolean(int input) {
return input != 0;
}
public boolean toBoolean(Object input) {
return (input != null) && (!input.equals(""));
}
}
and then:
boolean lt = LooselyTyped.toBoolean(yourvariable);

Categories

Resources