There is a question in Java past exam paper that brothers me:
With implicit conversion of primitive data types, you can lose precision and get incorrect results.
A True, B False
The key to the answer is A: True
I think it will neither lose precision nor get incorrect results. I know the explicit conversion can lose precision and get incorrect results but not implicit one.
For example:
int i = 9;
short s = 3;
i = s; // implicit conversion, neither loose
//precision nor incorrect results
s = i; // compile error, do we call this implicit conversion?
//if yes, then the answer to question 3 is True,
//but I don't think this is an implicit conversion,
//so I think answer is false.
As states on the notes:
Implicit type conversion: The programmer does not make any attempt to convert the type, rather the type is automatically converted by the system under certain circumstances.
Could anyone please advise?
Many thanks.
Answer = A
float f = Long.MAX_VALUE;
System.out.println(Long.MAX_VALUE);
System.out.printf("%.0f", f);
output
9223372036854775807
9223372036854776000
There are some cases where the compiler will allow an implicit conversion, but you may still lose precision. For example:
long a = Long.MAX_VALUE; // 9223372036854775807
double b = a; // 9223372036854776000
See the JLS for more details on this.
There is implicit conversions in assignment operators. These can lose precision or cause an overflow. For regular assignments, implicit conversion only happen when the compiler knows it is safe. It can still lose precision, but not cause an overflow.
e.g.
final int six = 6;
byte b = six; // compiler uses constant propagation and value is in range.
int five = 5;
byte b2 = five; // fails to compile
double d = 5.5;
five += d; // compiles fine, even though implicit conversion drops the 0.5
// five == 10 not 10.5
five += Double.NaN; // five is now 0 ;)
Related
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)
My question is two-part.
Why does the following work fine in Eclipse? Isn't "Double" a class?
Double h = 2.5;
double j = 2;
Why does "Double" above give me an error when I don't assign a decimal value to it, but "double" is fine whether or not I assign a decimal value to it?
As was already mentioned, the term is autoboxing. The object wrappers for the primitive types will automatically convert.
As to your second part,
Double a = 2;
Doesn't work since 2 is not a double and the auto boxing only works between the same types. In this case 2 is an int.
But if you cast it.
Double a = (double)2;
works just fine.
double a = 2;
works because an int can be automatically converted to a double. But going the
other way doesn't work.
int a = 2.2; // not permitted.
Check out the Section on conversions. In the Java Language Specification. Warning that it can sometimes be difficult to read.
Amended Answer.
In java you can cast up or down or have narrowing or widening casts (going from a 32 bit to 16 bit) value is narrowing. But I tend to think about it is losing vs not losing something. In most cases if you have the potential to lose part of value in assignment, you need to cast, otherwise you don't (See exceptions at end). Here are some examples.
long a = 2; // 2 is an integer but going to a long doesn't `lose` precision.
int b = 2L; // here, 2 is a long and the assignment is not permitted. Even
// though a long 2 will fit inside an int, the cast is still
// required.
int b = (int)2L; // Fine, but clearly a contrived case
Same for floating point.
float a = 2.2f; // fine
double b = a; // no problem, not precision lost
float c = b; // can't do it, as it requires a cast.
double c = 2.2f; // a float to a double, again a not problem.
float d = 2.2; // 2.2 is a double by default so requires a cast or the float designator.
float d = (float)2.2;
Exceptions
No cast is required when converting from int to float or long to double. However, precision can still be lost since the floats only have 24 bits of precision and doubles only have 53 bits of precision.
To see this for ints you can run the following:
for (int i = Integer.MAX_VALUE; i > Integer.MAX_VALUE-100; i--) {
float s = i;
int t = (int)s; // normal cast required
if (i != t) {
System.out.println (i + " " + t);
}
}
Double is a wrapper class, creating a new Double casts a primitive variable of the SAME type into a Object. For Double h = 2, you are wrapping a int into a Double. Since wrapping only works between same types, if you want your Double variable be 2, then you should use
Double h = 2.0;
New Java programmers are often confused by compilation error messages like:
"incompatible types: possible lossy conversion from double to int"
for this line of code:
int squareRoot = Math.sqrt(i);
In general, what does the "possible lossy conversion" error message mean, and how do you fix it?
First of all, this is a compilation error. If you ever see it in an exception message at runtime, it is because you have have run a program with compilation errors1.
The general form of the message is this:
"incompatible types: possible lossy conversion from <type1> to <type2>"
where <type1> and <type2> are both primitive numeric types; i.e. one of byte, char, short, int, long, float or double.
This error happens when your code attempts to do an implicit conversion from <type1> to <type2> but the conversion could be lossy.
In the example in the question:
int squareRoot = Math.sqrt(i);
the sqrt method produces a double, but a conversion from double to int is potentially lossy.
What does "potentially lossy" mean?
Well lets look at a couple of examples.
A conversion of a long to an int is a potentially lossy conversion because there are long values that do not have a corresponding int value. For example, any long value that is greater than 2^31 - 1 is too large to be represented as an int. Similarly, any number less than -2^31 is too small.
A conversion of an int to a long is NOT lossy conversion because every int value has a corresponding long value.
A conversion of a float to an long is a potentially lossy conversion because there float values that are outside of the range that can be represented as long values. Such numbers are (lossily) convert into Long.MAX_VALUE or Long.MIN_VALUE, as are NaN and Inf values.
A conversion of an long to a float is NOT lossy conversion because every long value has a corresponding float value. (The converted value may be less precise, but "lossiness" doesn't mean that ... in this context.)
These are all the conversions that are potentially lossy:
short to byte or char
char to byte or short
int to byte, short or char
long to byte, short, char or int
float to byte, short, char, int or long
double to byte, short, char, int, long or float.
How do you fix the error?
The way to make the compilation error go away is to add a typecast. For example;
int i = 47;
int squareRoot = Math.sqrt(i); // compilation error!
becomes
int i = 47;
int squareRoot = (int) Math.sqrt(i); // no compilation error
But is that really a fix? Consider that the square root of 47 is 6.8556546004 ... but squareRoot will get the value 6. (The conversion will truncate, not round.)
And what about this?
byte b = (int) 512;
That results in b getting the value 0. Converting from a larger int type to a smaller int type is done by masking out the high order bits, and the low-order 8 bits of 512 are all zero.
In short, you should not simply add a typecast, because it might not do the correct thing for your application.
Instead, you need to understand why your code needs to do a conversion:
Is this happening because you have made some other mistake in your code?
Should the <type1> be a different type, so that a lossy conversion isn't needed here?
If a conversion is necessary, is the silent lossy conversion that the typecast will do the correct behavior?
Or should your code be doing some range checks and dealing with incorrect / unexpected values by throwing an exception?
"Possible lossy conversion" when subscripting.
First example:
for (double d = 0; d < 10.0; d += 1.0) {
System.out.println(array[d]); // <<-- possible lossy conversion
}
The problem here is that array index value must be int. So d has to be converted from double to int. In general, using a floating point value as an index doesn't make sense. Either someone is under the impression that Java arrays work like (say) Python dictionaries, or they have overlooked the fact that floating-point arithmetic is often inexact.
The solution is to rewrite the code to avoid using a floating point value as an array index. (Adding a type cast is probably an incorrect solution.)
Second example:
for (long l = 0; l < 10; l++) {
System.out.println(array[l]); // <<-- possible lossy conversion
}
This is a variation of the previous problem, and the solution is the same. The difference is that the root cause is that Java arrays are limited to 32 bit indexes. If you want an "array like" data structure which has more than 231 - 1 elements, you need to define or find a class to do it.
"Possible lossy conversion" in method or constructor calls
Consider this:
public class User {
String name;
short age;
int height;
public User(String name, short age, int height) {
this.name = name;
this.age = age;
this.height = height;
}
public static void main(String[] args) {
User user1 = new User("Dan", 20, 190);
}
}
Compiling the above with Java 11 gives the following:
$ javac -Xdiags:verbose User.java
User.java:20: error: constructor User in class User cannot be applied to given types;
User user1 = new User("Dan", 20, 190);
^
required: String,short,int
found: String,int,int
reason: argument mismatch; possible lossy conversion from int to short
1 error
The problem is that the literal 20 is an int, and the corresponding parameter in the constructor is declared as a short. Converting an int to a short is lossy.
"Possible lossy conversion" in a return statement.
Example:
public int compute() {
long result = 42L;
return result; // <<-- possible lossy conversion
}
A return (with a value / expression) could be thought of an an "assignment to the return value". But no matter how you think about it, it is necessary to convert the value supplied to the actual return type of the method. Possible solutions are adding a typecast (which says "I acknowledge the lossy-ness") or changing the method's return type.
"Possible lossy conversion" due to promotion in expressions
Consider this:
byte b1 = 0x01;
byte mask = 0x0f;
byte result = b1 & mask; // <<-- possible lossy conversion
This will tell you that you that there is a "possible lossy conversion from int to byte". This is actually a variation of the first example. The potentially confusing thing is understanding where the int comes from.
The answer to that is it comes from the & operator. In fact all of the arithmetic and bitwise operators for integer types will produce an int or long, depending on the operands. So in the above example, b1 & mask is actually producing an int, but we are trying to assign that to a byte.
To fix this example we must type-cast the expression result back to a byte before assigning it.
byte result = (byte) (b1 & mask);
"Possible lossy conversion" when assigning literals
Consider this:
int a = 21;
byte b1 = a; // <<-- possible lossy conversion
byte b2 = 21; // OK
What is going on? Why is one version allowed but the other one isn't? (After all they "do" the same thing!)
First of all, the JLS states that 21 is an numeric literal whose type is int. (There are no byte or short literals.) So in both cases we are assigning an int to a byte.
In the first case, the reason for the error is that not all int values will fit into a byte.
In the second case, the compiler knows that 21 is a value that will always fit into a byte.
The technical explanation is that in an assignment context, it is permissible to perform a primitive narrowing conversion to a byte, char or short if the following are all true:
The value is the result of a compile time constant expression (which includes literals).
The type of the expression is byte, short, char or int.
The constant value being assigned is representable (without loss) in the domain of the "target" type.
Note that this only applies with assignment statements, or more technically in assignment contexts. Thus:
Byte b4 = new Byte(21); // incorrect
gives a compilation error.
1 - For instance, the Eclipse IDE has an option which allows you to ignore compilation errors and run the code anyway. If you select this, the IDE's compiler will create a .class file where the method with the error will throw an unchecked exception if it is called. The exception message will mention the compilation error message.
I have the following code in my project:
int percent = 2;
int count = 10;
int percentagefill = (percent/10)*count;
System.out.println(percentagefill);
Basically what is happening is that, I'm setting two variables, percent and count. I then calculate the percentage fill. For some strange reason the percentage fill is resulting in a 0, when in this case it should be 2. Any ideas why? Thanks in advance.
intdivided by int will still result in int. In this case:
(percent/10)*count
= (2/10)*10
= (0) * 10 <-- 0.2 is rounded down to 0
= 0
You can read this question for reference. Also, here's the Java spec where is says that integer division is rounded towards 0. As for the fix, as long as floating point precision does not become an issue, just use double as PaulP.R.O said.
You could divide by 10.0, or change int percent to double percent in order to force a conversion to double. Otherwise, you are getting integer division, which truncates off the decimal part.
Here is a relevant question: "Java Integer Division, How do you produce a double?"
if you really need the result to be an int, you could do the multiply before the divide to avoid the integer division giving you zero.
int percentagefill = (percent*count)/10;
Change this line:
int percentagefill = (percent/10)*count;
To:
double percentagefill = (percent/10.0)*count;
This will use floating point arithmetic (because of the 10.0 instead of 10), and store the result in a double (which has precision past the decimal point unlike an int).
As others have mentioned, you're losing precision with integer division
The solution depends on your needs: if your result needs to be an integer anyway, multiply first:
int percentagefill = (percent*count)/10;
Could be "good enough" for you (you'll get the correct answer rounded down).
If you need to be able to get fractional answers, you need to convert things to floating point types:
double percentagefill = (percent/10.0)*count;
// ^ the .0 makes this a double,
// forcing the division to be a
// floating-point operation.
It's easy to fix:
int percent = 2;
int count = 10;
double percentagefill = (percent/10.0)*count;
System.out.println(percentagefill);
Dave Newton, your answer should have been an answer. :)
The integer 2 / the integer 10 = 0.
The integer 0 * the integer 10 = 0.
You will need a float or double data type. While working with information, always be weary of chances for the interpreter to make data type assumptions and "casts". println takes an intefer and casts to a string for display is one example.
Most of my work is in php and when working with values, 0, NULL, ERROR can all be different things and can yield unexpected results. Sometimes you may need to explicitly cast a variable to a different data type to get the intended results.
This is so due to the fact that you are using the integer data type when you should be using a floating-point data type such as double. This code should result in 2.0:
double percent = 2;
double count = 10;
double percentagefill = (percent/10)*count;
System.out.println(percentagefill);
In Java programming language widen and boxing doesn't work, but how does it work in following example?
final short myshort = 10;
Integer iRef5 = myshort;
Why does this work? Is this not the same as widen and then box?
But if I write the following code:
final int myint = 10;
Long myLONG = myint;
why it doesn't work?
Following what others have said, I can confirm that I can compile your first example with the Eclipse compiler, but not the second. With the javac compiler, both don't compile, as stated by Vlad
This seems to be a bug in either compiler! Let's consult the JLS to find out, which one is right :-)
With java 7 both the examples are not working. you will get below exception:
Type mismatch: cannot convert from short to Integer
Type mismatch: cannot convert from int to Long
Because the problem is not because of boxing but because of conversion.
You can either widen or box, but you can't do both.
You can do
final int myint = 10;
Long myLONG = (long) myint;
Neither works as it is (using javac 1.6.0_26 from Sun/Oracle, on Linux).
See also here.
b.java:4: incompatible types
found : short
required: java.lang.Integer
Integer iRef5 = myshort;
^
b.java:7: incompatible types
found : int
required: java.lang.Long
Long myLONG = myint;
^
2 errors
Let's examine what you're trying to do in detail:
final short myshort = 10;
Integer iRef5 = myshort;
The compiler will try to first box that short into an object, so that it can then perform the assignment (it cannot widen directly, since it is dealing with different types: an object and a primitive).
In short, this is equivalent to:
final short myshort = 10;
final Short box = new Short(myshort); // boxing: so that objects are assignable.
Integer iRef5 = box; // widening: this fails as Integer is not a superclass of Short
The same reasoning can be applied to your second example (which also fails), as is visible here. If your compiler does not complain on the first one, then there might be a bug with the compiler, because this is what's defined in the JLS. See the complete set of rules for conversion/promotion in the JLS here.
These are the promotion rules that apply to expressions in Java
all byte and short values are promoted to int
If one operand is long,the whole expression is promoted to long
If one operand is a float ,the whole expression is promoted to float
If one operand is double ,the whole expression is promoted to double
Hence short value is promoted to int. This is not widening.
"The conversion of a subtype to one of its supertypes is called widening"
You can box then wide but you can't widen and then box
So
final short myshort = 10;
Integer iRef5 = myshort;
is equivalent to
final short myshort = 10;
Integer iRef5 = 10;
which is perfectly valid
but
Long myLONG = 10;//this won't compile ,
But try with 10L it will so it will box then you can have it object too i.e. Object o = 10L;
See also
boxing-and-widening
scjp-cant-widen-and-then-box-but-you-can-box-and-then-widen