我试图追踪一些非常奇怪的Java行为。我有一个涉及double的公式,但是“保证”给出一个整数答案 - 具体来说,是一个无符号的32位整数(唉,Java不能很好)。不幸的是,我的回答有时是不正确的。
最终我发现了这个问题,但对我来说行为仍然非常奇怪:double
直接投放到int
似乎被限制在{{ 1}}对于有符号整数,而MAX_INT
强制转换为double
然后强制转换为long
会得到预期答案(-1;无符号32位整数的MAX INT,表示为带符号的32位整数。)
我写了一个小测试程序:
int
当我运行这个小程序时,我得到了:
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);
}
底部两行是我想要了解的。双重演员给了我预期的“-1”;但直接转换为MAX_INT提供了一个32位有符号整数。来自C ++背景,我会理解它是否给了我一个“奇数”而不是预期的-1(又名“天真的演员”),但这让我感到困惑。
那么,接下来的问题是:Java中的这种“预期”行为(例如直接转移到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}}任何double
会被“限制”为int
)吗?对任何意外类型进行强制转换吗?例如,我希望MAX_INT
和short
类似;但是当把一个超大双倍的东西放到浮动状态时,“预期的行为”是什么?
谢谢!
答案 0 :(得分:11)
这是预期的行为。请记住,Java中没有原始的unsigned long或int类型,并且用于Narrowing原始转换的Java Language Specification(Java 7)(5.1.3)声明抛出“太小或太大”的浮点值(是它是double或float)到整数类型的int或long将使用有符号整数类型的最小值或最大值(强调我的):
将浮点数转换为整数类型 T需要两个步骤:
在第一步中,浮点数转换为 一个long,如果T很长,或者一个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类型的最大可表示值。 *
- 醇>
在第二步中:*如果T为int或long,则转换结果是第一步的结果。 *如果T是byte,char或 简而言之,转换的结果是缩小的结果 转换为第一步结果的类型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是十六进制,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。
答案 1 :(得分:3)
这就是语言规范的编写方式。将浮点数转换为整数类型,如果该值对于目标而言太大,则替换最大值。在从一个整数类型到较小整数类型的缩小转换中,高位比特被丢弃。
请参阅JLS 5.1.3. Narrowing Primitive Conversion
因此,标题中问题的答案是“是”。