Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do you need to place an 'f' at the end of the decimal number when declaring a float variable?

Tags:

java

I have seen this question asked before. However, I am not satisfied with the answers that are given. Typical responses are that Java sees the number as a double, since that is the default in JAVA, and gives a mismatch error on compiling.

This behavior is apparently totally ignoring my use of a float declaration.

My question is: If I am declaring a variable type as float, why does Java think it is a double? I am specifically declaring:

float x = 3.14;

I have told Java that I want this variable to be a float type by using the float keyword. Why is this required?

float x = 3.14f;

If I want a double type variable, I use the double keyword specifically declaring the variable as a double.

double x = 3.14;

Why does Java not recognize this when declaring a variable and insists the literal needs to be cast from a double to a float by adding the 'f' at the end? Shouldn't Java recognize the use of the keyword float and properly assign the type to the literal?

like image 419
Michael Sudduth Avatar asked Feb 11 '21 15:02

Michael Sudduth


2 Answers

Size matters

32-bit float → float x = 3.14 ; ← 64-bit double

The default for a fractional numeric literal in Java is the 64-bit primitive double type, as shown in the Answer by Mark Rotteveel. 3.14 is a double, as is 3.14d and 3.14D.

The primitive float type is smaller, 32-bit.

If Java were to infer that what you said is a double but what you meant is a float, Java would have to reduce the 64-bit value to a 32-bit value, discarding half the data, 32-bits. This could involve data loss for larger numbers. And this would definitely mean a loss of capacity.

So the designers of Java decided to be conservative in this matter. They decided to not be so presumptive as to throw away half your data, nor to reduce your specified capacity by half. They decided to take you at your word — if you said 64-bit double, they give you a double.

If you then go on to say you want to cram that 64-bit number into a 32-bit container, they flag the error, as 64-bits does not fit into 32-bits. Square peg, round hole.

round hole, 32-bit float → float x = 3.14 ; ← 64-bit double, square peg

Java does support the converse, a widening conversion. You can put a 32-bit float into a 64-bit double. And you can put a 32-bit int into a 64-bit long. These operations are safe, as the extra 32-bits can be filled safely with zeros. So Java is willing and able to support these operations.

Be explicit

I suggest you make a habit of being explicit. Do not rely on the default. If you mean a 32-bit float, append the f or F to your literal. If you mean a 64-bit double, append the d or D to your literal.

Doing so not only quiets the compiler, it makes your intentions clear to the humans reading your code.

float pi = 3.14F ;
double pi = 3.14d ;

BigDecimal

And, by the way, if you care about accuracy rather than performance, you’ll be using BigDecimal rather than float/double. In which case this issue is moot.

BigDecimal pi = new BigDecimal( "3.14" ) ;
like image 132
Basil Bourque Avatar answered Nov 14 '22 23:11

Basil Bourque


The reason is that the Java Language Specification specifies that a floating point literal without the f or F suffix, is a double. From Floating-Point Literals in the Java 15 Language Specification:

A floating-point literal is of type float if it is suffixed with an ASCII letter F or f; otherwise its type is double and it can optionally be suffixed with an ASCII letter D or d.

Java doesn't try to infer the type of the literal based on the type of the variable you're assigning to. A double-literal is simply always a double. In other words, semantically, it behaves the same as if your code did:

double temp = 3.14;
float x = temp;

Which will also fail to compile, because assigning a double to a float can result in loss of information, if the magnitude of the value is too large to fit in a float. This is known as narrowing primitive conversion, and requires an explicit cast to tell the compiler you're aware of the risk and you accept it.

In some cases, it may seem as if Java will perform inference of type from the variable (e.g. when assigning an int or long literal to a double or float variable), but that is because the entire range of int and long can be stored in a float or double (possibly with loss of precision). This is known as widening primitive conversion.

like image 24
Mark Rotteveel Avatar answered Nov 14 '22 21:11

Mark Rotteveel