首页 文章

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

提问于
浏览
8

我试图找出一些非常奇怪的Java行为 . 我有一个涉及double的公式,但是“保证”给出一个整数答案 - 具体来说,是一个无符号的32位整数(唉,Java不能很好) . 不幸的是,我的答案有时不正确 .

最终我发现了这个问题,但对我来说这个行为仍然很奇怪:一个 double 直接转换为 int 似乎被限制在 MAX_INT 为有符号整数,而 double 转换为 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中的"expected"行为(例如,任何 double 直接转换为 int 将"capped"到 MAX_INT )?对任何意外类型进行强制转换吗?我希望它与 shortbyte 类似,例如;但什么是'expected behavior'什么时候铸造超大双倍浮动?

谢谢!

2 回答

  • 3

    这是预期的行为 . 请记住,Java中没有原始的unsigned long或int类型,并且Java Language Specification(Java 7)for narrowrowing primitive conversion(5.1.3)声明将"too small or too large"浮点值(可以是double或float)转换为整数类型int或long将使用有符号整数类型的最小值或最大值(强调我的):

    将浮点数转换为整数类型T需要两个步骤:在第一步中,如果T为long,则将浮点数转换为long,如果T为byte,则转换为int ,short,char或int,如下所示:如果浮点数为NaN(§4.2.3),则转换的第一步结果为int或long 0.否则,如果是浮点数如果不是无穷大,浮点值将四舍五入为整数值V,使用IEEE 754舍入为零的模式(§4.2.3)向零舍入 . 然后有两种情况:a . 如果T很长,并且该整数值可以表示为long,则第一步的结果是长值V. b . 否则,如果此整数值可以表示为int,则第一步的结果是int值V.否则,以下两种情况之一必须为true:a . 该值必须太小(大幅度或负无穷大的负值),第一步的结果是int或long类型的最小可表示值 . 湾该值必须太大(大幅度或正无穷大的正值),第一步的结果是int或long类型的最大可表示值 . *在第二步中:*如果T为int或long,则转换结果是第一步的结果 . *如果T是byte,char或short,则转换结果是第一步结果转换为类型T(第5.1.3节)的结果 . 例5.1.3-1 . 缩小原始转换类测试{
    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:”(短)fmin“..”(短)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
    短:0 ..- 1
    char:0..65535
    字节: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 .

  • 11

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

    见JLS 5.1.3. Narrowing Primitive Conversion

    所以, Headers 中问题的答案是“是” .

相关问题