tags:

views:

1138

answers:

3

Every time I think I understand about casting and conversions, I find another strange behavior.

long l = 123456789L;
float f = l;
System.out.println(f);  // outputs 1.23456792E8

Given that a long has greater bit-depth than a float, I would expect that an explicit cast would be required in order for this to compile. And not surprisingly, we see that we have lost precision in the result.

Why is a cast not required here?

+7  A: 

The Java Language Specification, Chapter 5: Conversion and Promotion addresses this issue:

5.1.2 Widening Primitive Conversion

The following 19 specific conversions on primitive types are called the widening primitive conversions:

  • byte to short, int, long, float, or double
  • short to int, long, float, or double
  • char to int, long, float, or double
  • int to long, float, or double
  • long to float or double
  • float to double

Widening primitive conversions do not lose information about the overall magnitude of a numeric value.

...

Conversion of an int or a long value to float, or of a long value to double, may result in loss of precision-that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value

To put it another way, the JLS distinguishes between a loss of magnitude and a loss of precision.

int to byte for example is a (potential) loss of magnitude because you can't store 500 in a byte.

long to float is a potential loss of precision but not magnitude because the value range for floats is larger than that for longs.

So the rule is:

  • Loss of magnitude: explicit cast required;
  • Loss of precision: no cast required.

Subtle? Sure. But I hope that clears that up.

cletus
I think it would be reasonable to argue that conversions which may lose information require a specific cast. These *are* lossy conversions, which may well surprise developers. I can see that it's convenient in some situations, but inconvenient in others. I don't think it's at all unreasonable to ask about this potentially confusing choice though.
Jon Skeet
Any conversion between a double or float and **any** integer type (to or from) may result in a loss of precision. That would require an explicit cast in any case. IMHO that's not necessary.
cletus
@cletus: By that logic, why would a cast *ever* be necessary?
jalf
Would int to double potentially lose information? I thought every integer would be exactly represented by a double. Obviously going the other way would always be potentially lossy. Given your reasoning, why should `double` to `long` be explicit, as it currently is? The intuitive (IMO) reasoning is "be explicit if you might lose information" - but that doesn't *quite* hold here.
Jon Skeet
I'm with jalf, it seems you against casting entirely.
FarmBoy
The JLS makes the distinction between magnitude and precision. Loss of magnitude = explicit cast required. Loss of precision = not required. As for long to double what about 10L to double?
cletus
@JN: I'm not against casting entirely. I'm just against spurious casting.
cletus
@cletus: I think the point is that that distinction is a pretty subtle one, whereas "doesn't lose any information" is a lot simpler to understand.
Jon Skeet
+8  A: 

The same question could be asked of long to double - both conversions may lose information.

Section 5.1.2 of the Java Language Specification says:

Widening primitive conversions do not lose information about the overall magnitude of a numeric value. Indeed, conversions widening from an integral type to another integral type do not lose any information at all; the numeric value is preserved exactly. Conversions widening from float to double in strictfp expressions also preserve the numeric value exactly; however, such conversions that are not strictfp may lose information about the overall magnitude of the converted value.

Conversion of an int or a long value to float, or of a long value to double, may result in loss of precision-that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value, using IEEE 754 round-to-nearest mode (§4.2.4).

In other words even though you may lose information, you know that the value will still be in the overall range of the target type.

The choice could certainly have been made to require all implicit conversions to lose no information at all - so int and long to float would have been explicit and long to double would have been explicit. (int to double is okay; a double has enough precision to accurately represent all int values.)

In some cases that would have been useful - in some cases not. Language design is about compromise; you can't win 'em all. I'm not sure what decision I'd have made...

Jon Skeet
Yes, double has 52 bits mantissa, that is more than enough to exactly represent 32 bit integers.
starblue
+2  A: 

Though you're correct that a long uses more bits internally than a float, the java language works on a widening path:

byte -> short -> int -> long -> float -> double

To convert from left to right (a widening conversion), there is no cast necessary (which is why long to float is allowed). To convert right to left (a narrowing conversion) an explicit cast is necessary.

Peter Nix