What Does "Possible Lossy Conversion" Mean and How to Fix It

What does possible lossy conversion mean and how do I 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.

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

  2. A conversion of an int to a long is NOT lossy conversion because every int value has a corresponding long value.

  3. A conversion of a float to an long is a potentially lossy conversion because there float values that are too large or too small to represent as long values.

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

possible lossy conversion error when trying to add two floats?

"Lossy" in this context means you get a truncation and lose information. Like 1.11 (float) will become 1 (int), and you lose 0.11. Java is very strict about types (unlike many other language), which can be annoying.

To fix this error change your function return type to float like this:

public static float addition (float num1, float num2) {
float sum = num1 + num2;
return sum;
}

Also, to be consistent, re-declare your numbers to be float:

float numa = 2.0f;
float numb = 3.0f;

Error: incompatible types: possible lossy conversion from double to float

You need to cast. Or declare area as double.

float area = (float)(Math.sqrt(5 * (5 + 2 *
(Math.sqrt(5))) * l * l) / 4);

or

double area = Math.sqrt(5 * (5 + 2 *
(Math.sqrt(5))) * l * l) / 4;

Aside: you are mixing integers and floating point in the same equation. Often this leads to disaster. It's probably better to use double literals.

double area = Math.sqrt(5.0 * (5.0 + 2.0 *
(Math.sqrt(5.0))) * l * l) / 4.0;


Related Topics



Leave a reply



Submit