Java整数类型原语是否在转换类型的MAX_INT处“加盖”?

我试图追踪一些非常奇怪的Java行为。 我有一个涉及double的公式,但是“保证”给出一个整数答案 – 特别是一个无符号的32位整数(唉,Java不能很好)。 不幸的是,我的答案有时不正确。

最终我发现了这个问题,但对我来说这种行为仍然很奇怪:对于一个有符号的整数,直接对一个int进行double转换似乎被限制在MAX_INT ,而对于一个long则转换为一个long然后转换为一个int给出了预期的答案(-1;无符号32位整数的MAX INT,表示为带符号的32位整数)。

我写了一个小测试程序:

 public static void main(String[] args) { // This is the Max Int for a 32-bit unsigned integer double maxUIntAsDouble = 4294967295.00; long maxUintFromDoubleAsLong = (long)maxUIntAsDouble; long maxUintFromDoubleAsInt = (int)maxUIntAsDouble; int formulaTest = (int) (maxUintFromDoubleAsLong * 1.0); int testFormulaeWithDoubleCast = (int)((long) (maxUintFromDoubleAsLong * 1.0)); // This is a more-or-less random "big number" long longUnderTest = 4123456789L; // Max int for a 32-bit unsigned integer long longUnderTest2 = 4294967295L; int intFromLong = (int) longUnderTest; int intFromLong2 = (int) longUnderTest2; System.out.println("Long is: " + longUnderTest); System.out.println("Translated to Int is:" + intFromLong); System.out.println("Long 2 is: " + longUnderTest2); System.out.println("Translated to Int is:" + intFromLong2); System.out.println("Max UInt as Double: " + maxUIntAsDouble); System.out.println("Max UInt from Double to Long: " + maxUintFromDoubleAsLong); System.out.println("Max UInt from Double to Int: " + maxUintFromDoubleAsInt); System.out.println("Formula test: " + formulaTest); System.out.println("Formula Test with Double Cast: " + testFormulaeWithDoubleCast); } 

当我运行这个小程序时,我得到:

 Long is: 4123456789 Translated to Int is:-171510507 Long 2 is: 4294967295 Translated to Int is:-1 Max UInt as Double: 4.294967295E9 Max UInt from Double to Long: 4294967295 Max UInt from Double to Int: 2147483647 // MAX INT for an unsigned int Formula test: 2147483647 // Binary: all 1s, which is what I expected Formula Test with Double Cast: -1 

最下面的两行是我想要了解的。 双重演员给了我预期的“-1”; 但直接转换为MAX_INT提供了一个32位有符号整数。 来自C ++背景,我会理解它是否给了我一个“奇数”而不是预期的-1(又名“天真的演员”),但这让我感到困惑。

那么,接下来的问题是:这是Java中的“预期”行为(例如,任何直接转换为int double转换都将“限制”到MAX_INT )? 对任何意想不到的类型进行强制转换吗? 例如,我希望它与shortbyte类似; 但是当把一个超大双倍的浮动时,“预期的行为”是什么?

谢谢!

这是预期的行为。 请记住,Java中没有原始的unsigned long或int类型,并且用于Narrowing原语转换的Java语言规范 (Java 7)(5.1.3)声明抛出“太小或太大”的浮点值(是双倍的)或浮点数到整数类型的int或long将使用有符号整数类型的最小值或最大值(强调我的):

将浮点数转换为整数类型T需要两个步骤:

  1. 在第一步中,浮点数转换为long(如果T为long)或转换为int(如果T为byte,short,char或int),如下所示:

    • 如果浮点数为NaN(§4.2.3),则转换的第一步结果为int或long 0。
    • 否则,如果浮点数不是无穷大,则浮点值将四舍五入为整数值V,使用IEEE 754舍入为零的模式(§4.2.3)向零舍入。 然后有两种情况:

      • 一个。 如果T很长,并且该整数值可以表示为long,则第一步的结果是长值V.
      • 湾 否则,如果此整数值可以表示为int,则第一步的结果是int值V.
    • 否则,以下两种情况之一必须为真:

      • 一个。 该值必须太小(大幅度或负无穷大的负值),第一步的结果是int或long类型的最小可表示值。
      • 该值必须太大(大幅度或正无穷大的正值),第一步的结果是int或long类型的最大可表示值。 *
  2. 在第二步中:*如果T为int或long,则转换结果是第一步的结果。 *如果T是byte,char或short,则转换结果是第一步结果转换为类型T(第5.1.3节)的结果。

例5.1.3-1。 缩小原始转换

 class Test { public static void main(String[] args) { float fmin = Float.NEGATIVE_INFINITY; float fmax = Float.POSITIVE_INFINITY; System.out.println("long: " + (long)fmin + ".." + (long)fmax); System.out.println("int: " + (int)fmin + ".." + (int)fmax); System.out.println("short: " + (short)fmin + ".." + (short)fmax); System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax); System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax); } } 

该程序产生输出:

 long: -9223372036854775808..9223372036854775807 int: -2147483648..2147483647 short: 0..-1 char: 0..65535 byte: 0..-1 

char,int和long的结果并不令人惊讶,产生了该类型的最小和最大可表示值。

字节和短的结果会丢失有关数值的符号和大小的信息,也会丢失精度。 通过检查最小和最大int的低位比特可以理解结果。 最小int是hex,0x80000000,最大int是0x7fffffff。 这解释了短结果,即这些值的低16位,即0x0000和0xffff; 它解释了char结果,它们也是这些值的低16位,即’\ u0000’和’\ uffff’; 它解释了字节结果,这些值是这些值的低8位,即0x00和0xff。

第一种情况int formulaTest = (int) (maxUintFromDoubleAsLong * 1.0); 因此通过乘法将maxUintFromDoubleAsLong提升为double,然后将其强制转换为int。 由于该值太大而无法表示为有符号整数,因此该值变为2147483647(Integer.MAX_VALUE)或0x7FFFFFFF。

至于后一种情况:

将有符号整数缩小到整数类型T只会丢弃除n个最低位之外的所有位,其中n是用于表示类型T的位数。除了可能丢失有关数值大小的信息之外,这可能导致结果值的符号与输入值的符号不同。

所以int testFormulaeWithDoubleCast = (int)((long) (maxUintFromDoubleAsLong * 1.0)); 首先将maxUintFromDoubleAsLong提升为double,返回long(仍然适合)然后再提升为int。 在最后一次转换中,多余的位被简单地删除,留下0xFFFFFFFF,当被解释为有符号整数时为-1。

这就是语言规范的编写方式。 将浮点数转换为整数类型,如果该值对于目标而言太大,则替换最大值。 在从一个整数类型到较小整数类型的缩小转换中,丢弃高位比特。

请参阅JLS 5.1.3。 缩小原始转换

所以,标题中问题的答案是“是”。