Declaring Floats, Why Default Type Double

Declaring floats, why default type double?

Why is the default type a double?

That's a question that would be best asked of the designers of the Java language. They are the only people who know the real reasons why that language design decision was made. But I expect that the reasoning was something along the following lines:

They needed to distinguish between the two types of literals because they do actually mean different values ... from a mathematical perspective.

Supposing they made "float" the default for literals, consider this example

// (Hypothetical "java" code ... )
double d = 0.1;
double d2 = 0.1d;

In the above, the d and d2 would actually have different values. In the first case, a low precision float value is converted to a higher precision double value at the point of assignment. But you cannot recover precision that isn't there.

I posit that a language design where those two statements are both legal, and mean different things is a BAD idea ... considering that the actual meaning of the first statement is different to the "natural" meaning.

By doing it the way they've done it:

double d = 0.1f;
double d2 = 0.1;

are both legal, and mean different things again. But in the first statement, the programmer's intention is clear, and the second statement the "natural" meaning is what the programmer gets. And in this case:

float f = 0.1f;
float f2 = 0.1; // compilation error!

... the compiler picks up the mismatch.


I am guessing using floats is the exception and not the rule (using doubles instead) with modern hardware so at some point it would make sense to assume that the user intends 0.1f when he writes float f = 0.1;

They could do that already. But the problem is coming up with a set of type conversion rules that work ... and are simple enough that you don't need a degree in Java-ology to actually understand. Having 0.1 mean different things in different context would be confusing. And consider this:

void method(float f) { ... }
void method(double d) { ... }

// Which overload is called in the following?
this.method(1.0);

Programming language design is tricky. A change in one area can have consequences in others.


UPDATE to address some points raised by @supercat.

@supercat: Given the above overloads, which method will be invoked for method(16777217)? Is that the best choice?

I incorrectly commented ... compilation error. In fact the answer is method(float).

The JLS says this:

15.12.2.5. Choosing the Most Specific Method

If more than one member method is both accessible and applicable to a
method invocation, it is necessary to choose one to provide the
descriptor for the run-time method dispatch. The Java programming
language uses the rule that the most specific method is chosen.

...

[The symbols m1 and m2 denote methods that are applicable.]

[If] m2 is not generic, and m1 and m2 are applicable by strict or
loose invocation, and where m1 has formal parameter types S1, ..., Sn
and m2 has formal parameter types T1, ..., Tn, the type Si is more
specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).

...

The above conditions are the only circumstances under which one method
may be more specific than another.

A type S is more specific than a type T for any expression if S <: T
(§4.10).

In this case, we are comparing method(float) and method(double) which are both applicable to the call. Since float <: double, it is more specific, and therefore method(float) will be selected.

@supercat: Such behavior may cause problems if e.g. an expression
like int2 = (int) Math.Round(int1 * 3.5) or long2 = Math.Round(long1 * 3.5) gets replaced with int1 = (int) Math.Round(int2 * 3) or long2 = Math.Round(long1 * 3)

The change would look harmless, but the first two expressions are
correct up to 613566756 or 2573485501354568 and the latter two
fail above 5592405 [the last being completely bogus above
715827882].

If you are talking about a person making that change ... well yes.

However, the compiler won't make that change behind your back. For example, int1 * 3.5 has type double (the int is converted to a double), so you end up calling the Math.Round(double).

As a general rule, Java arithmetic will implicitly convert from "smaller" to "larger" numeric types, but not from "larger" to "smaller".

However, you do still need to be careful since (in your rounding example):

  • the product of a integer and floating point may not be representable with sufficient precision because (say) a float has fewer bits of precision than an int.

  • casting the result of Math.round(double) to an integer type can result in conversion to the smallest / largest value of the integer type.

But all of this illustrates that arithmetic support in a programming language is tricky, and there are inevitable gotcha's for a new or unwary programmer.

Why floating point number 333.50 is double

by default floating point literal is double in java , if you want the value to float you may do this

float f = 333.5f

Java: Why we need to cast a float but not a double?

A floating-point literal is of type float if it ends with the letter F or f; otherwise its type is double and it can optionally end with the letter D or d.

The floating point types (float and double) can also be expressed using E or e (for scientific notation), F or f (32-bit float literal) and D or d (64-bit double literal; this is the default and by convention is omitted).

double d1 = 123.4;
// same value as d1, but in scientific notation
double d2 = 1.234e2;
float f1 = 123.4f;

See Floating-Point Literals in oracle Java Tutorial

Why is the f required when declaring floats?

Your declaration of a float contains two parts:

  1. It declares that the variable timeRemaining is of type float.
  2. It assigns the value 0.58 to this variable.

The problem occurs in part 2.

The right-hand side is evaluated on its own. According to the C# specification, a number containing a decimal point that doesn't have a suffix is interpreted as a double.

So we now have a double value that we want to assign to a variable of type float. In order to do this, there must be an implicit conversion from double to float. There is no such conversion, because you may (and in this case do) lose information in the conversion.

The reason is that the value used by the compiler isn't really 0.58, but the floating-point value closest to 0.58, which is 0.57999999999999978655962351581366... for double and exactly 0.579999946057796478271484375 for float.

Strictly speaking, the f is not required. You can avoid having to use the f suffix by casting the value to a float:

float timeRemaining = (float)0.58;

Float and double datatype in Java

The Wikipedia page on it is a good place to start.

To sum up:

  • float is represented in 32 bits, with 1 sign bit, 8 bits of exponent, and 23 bits of the significand (or what follows from a scientific-notation number: 2.33728*1012; 33728 is the significand).

  • double is represented in 64 bits, with 1 sign bit, 11 bits of exponent, and 52 bits of significand.

By default, Java uses double to represent its floating-point numerals (so a literal 3.14 is typed double). It's also the data type that will give you a much larger number range, so I would strongly encourage its use over float.

There may be certain libraries that actually force your usage of float, but in general - unless you can guarantee that your result will be small enough to fit in float's prescribed range, then it's best to opt with double.

If you require accuracy - for instance, you can't have a decimal value that is inaccurate (like 1/10 + 2/10), or you're doing anything with currency (for example, representing $10.33 in the system), then use a BigDecimal, which can support an arbitrary amount of precision and handle situations like that elegantly.

Why floating point value such as 3.14 are considered as double by default in MSVC?

That's what the C++ (and C) standard decided. Floating point literals are of type double, and if you need them to be floats, you suffix them with a f. There doesn't appear to be any specifically stated reason as to why, but I'd guess it's a) For compatibility with C, and b) A trade-off between precision and storage.

2.13.3 Floating literals The type
of a floating literal is double unless
explicitly specified by a suffix. The
suffixes f and F specify float, the suffixes l and L specify long double.
If the scaled value is not in the
range of
representable values for its type, the program is ill-formed.

Need help on understanding why this code doesn't compile

Ignoring char, Java will promote numeric types like this:

byte > short > int > long > float > double

These are called widening conversions. See JLS §5.1.2. Widening Primitive Conversion for detail.

Binary operators will promote to int, long, float, or double, whichever is nearest from the two values of the operator, i.e. result will never be byte or short. Example: byte + short will promote both sides to int. See JLS §5.6.2. Binary Numeric Promotion for detail.

The assignment operator will also do widening conversion of the value, with the extra rule that a constant expression of type byte, short, or int will go through a narrowing conversion if the type of the variable is byte or short, and the value of the constant expression is representable in the type. Note, there is no rule for narrowing double constant to float. See JLS §5.2. Assignment Contexts for detail.

So, for your code:

double x = 39.21; // double constant  to  double  (identity conversion)
float y = 2.1; // fails because double constant cannot promote to float

If code had compiled, what is the data type of x + y?

x + y  // double + float  promotes to  double

Answer: double

Next part:

short x = 14;  // narrowing conversion of  int constant  to  short
float y = 13; // widening conversion of int constant to float
double z = 30; // widening conversion of int constant to double

Now, what is the data type of x * y / z?

x * y        // short * float  promotes to  float
(x * y) / z // (float) / double promotes to double

Answer: double

Why does auto deduce this variable as double and not float?

Type of literal 3.5 is double. For float please use 3.5f

You can play with this snippet to see various type information.

why f is placed after float values?

By default 12.3 is double literal. To tell compiler to treat it as float explicitly -> use f or F.

See tutorial page on the primitive types.



Related Topics



Leave a reply



Submit