51

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?

Eric Wilson
  • 57,719
  • 77
  • 200
  • 270

4 Answers4

53

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
  • 616,129
  • 168
  • 910
  • 942
  • 6
    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 Aug 18 '09 at 13:34
  • 1
    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 Aug 18 '09 at 13:36
  • @cletus: By that logic, why would a cast *ever* be necessary? – jalf Aug 18 '09 at 13:37
  • 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 Aug 18 '09 at 13:39
  • I'm with jalf, it seems you against casting entirely. – Eric Wilson Aug 18 '09 at 13:43
  • 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 Aug 18 '09 at 13:44
  • @JN: I'm not against casting entirely. I'm just against spurious casting. – cletus Aug 18 '09 at 13:45
  • @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 Aug 18 '09 at 13:53
  • If one accepts the notion that floating-point conversions may sometimes cause things to erroneously compare as indistinguishable, but should not reverse the rank of things which do not, then (float)`1E39` will be correctly be regarded as indistinguishable from anything bigger (preserving magnitude), but casting it to `double` will yield a value which will erroneously compare greater than 1E308. That's a *hundreds-of-orders-of-magnitude* error. – supercat Jun 05 '13 at 17:16
  • 1
    @cletus : Loss of precision: no cast required. If this is the case, why cant we do float a = 3.6? Where 3.6 is interpreted as a double. – Vivin Jul 10 '15 at 20:31
53

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
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Yes, double has 52 bits mantissa, that is more than enough to exactly represent 32 bit integers. – starblue Aug 18 '09 at 15:31
  • Just to add to this, float has a max value of approx 3E38 while int is 2147483647 for the same number of bits. If you pass this max value to an int , print it, then pass it to a float and print it again; both value might be different that is "you may lose information". long is approx 9E18 – Ced Jun 13 '16 at 00:35
19

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
  • 8,545
  • 1
  • 27
  • 24
5

Somewhere I heard this. Float can store in exponential form as is we write it. '23500000000' is stored as '2.35e10' .So, float has space to occupy the range of values of long. Storing in exponential form is also the reason for precision loss.

george
  • 127
  • 2
  • 5
  • Given that this question is over five years old, and has several answers with upvotes, on addition to substantial discussion, it seems that you ought to explain how you are providing new information and/or how the other answers are insufficient. – Eric Wilson Mar 25 '15 at 15:17
  • 3
    @EricWilson None of the other answers seem to talk about why a `float`, 4 bytes, can hold the magnitude of an 8 byte `long`, while sacrificing precision. This answer doesn't fully answer that question either, but does add to the discussion. It might be better suited as a comment. One would need to reference and comprehend the [IEEE standard for floating points](https://en.wikipedia.org/wiki/IEEE_floating_point) I think to truly understand how this is possible. – crush Aug 17 '15 at 19:15
  • @crush Amazing how a question of no real importance from six years ago (tomorrow) draws interest today. – Eric Wilson Aug 17 '15 at 19:41
  • 2
    @EricWilson As stated by Jon Skeet, "These are lossy conversions, which may well surprise developers". This question is still drawing interest today because it ***surprises*** developers. – crush Aug 17 '15 at 19:55
  • @crush My perspective is that the real answer here is "That's the way Java does it." That's why I'm surprised when I see activity here. – Eric Wilson Aug 17 '15 at 19:57