关于转换:为什么Java隐式(没有强制转换)将`long`转换为`float`?

Why does Java implicitly (without cast) convert a `long` to a `float`?

每次我认为我了解铸造和转化,我发现另一个奇怪的行为。

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

考虑到longfloat具有更大的位深度,我希望需要显式的强制转换才能编译它。不足为奇,我们看到我们在结果中失去了精确性。

为什么这里不需要演员表?


同样的问题也可以问到longdouble-两种转换都可能丢失信息。

Java语言规范的第5.1.2节说明:

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

换句话说,即使您可能会丢失信息,您也知道该值仍将在目标类型的整个范围内。

当然,我们可以选择要求所有的隐式转换完全不丢失任何信息,因此,intlongfloat应该是显式的,longdouble应该是显式的。(intdouble是可以的;double有足够的精度精确地表示所有int值。)

在某些情况下是有用的——在某些情况下不是。语言设计是关于妥协的,你不能全部取胜。我不知道我会做出什么决定…


Java语言规范,第5章:转换和推广解决了这个问题:

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

换句话说,JLS区分了量级损失和精度损失。

例如,intbyte是一个(潜在的)数量级损失,因为你不能在byte中存储500个。

longfloat是一个潜在的精度损失,但不是数量级,因为浮动的值范围大于长的值范围。

所以规则是:

  • 损失量:需要明确的铸造;
  • 精度损失:无需铸造。

微妙?当然。但我希望能澄清这一点。


虽然你很正确,一个长的内部使用的比特多于一个浮点,但是Java语言在一个拓宽的路径上工作:

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

要从左向右转换(加宽转换),不需要强制转换(这就是允许长时间浮动的原因)。要从右向左转换(缩小转换),需要显式转换。


在我听到的某个地方。float可以以指数形式存储,就像我们写的那样。23500000000存储为"2.35e10"。因此,float有空间占据long值的范围。以指数形式存储也是造成精度损失的原因。