2

这是一个左移 的short值的Java 程序2。我有两个问题:

为什么16 到 29 之间num << i的值会产生有效结果?iAshort应该只保存从 -32,768 到 32,767 的值。

所以我想,也许System.out.println解释num << i为一个大于 a 的值short。但是为什么它会在 开始产生意想不到的值num << 30呢?

public class LeftShift {

    public static void main(String[] args)
    {
        short num = 2;
        for (int i = 0; i < 32; i++) {
            System.out.println("Left-shifting 2 by " + i + " places yields: " 
                                + (num << i));
        }
    }

}

输出:

Left-shifting 2 by 0 places yields: 2
Left-shifting 2 by 1 places yields: 4
Left-shifting 2 by 2 places yields: 8
Left-shifting 2 by 3 places yields: 16
Left-shifting 2 by 4 places yields: 32
Left-shifting 2 by 5 places yields: 64
Left-shifting 2 by 6 places yields: 128
Left-shifting 2 by 7 places yields: 256
Left-shifting 2 by 8 places yields: 512
Left-shifting 2 by 9 places yields: 1024
Left-shifting 2 by 10 places yields: 2048
Left-shifting 2 by 11 places yields: 4096
Left-shifting 2 by 12 places yields: 8192
Left-shifting 2 by 13 places yields: 16384
Left-shifting 2 by 14 places yields: 32768
Left-shifting 2 by 15 places yields: 65536
Left-shifting 2 by 16 places yields: 131072
Left-shifting 2 by 17 places yields: 262144
Left-shifting 2 by 18 places yields: 524288
Left-shifting 2 by 19 places yields: 1048576
Left-shifting 2 by 20 places yields: 2097152
Left-shifting 2 by 21 places yields: 4194304
Left-shifting 2 by 22 places yields: 8388608
Left-shifting 2 by 23 places yields: 16777216
Left-shifting 2 by 24 places yields: 33554432
Left-shifting 2 by 25 places yields: 67108864
Left-shifting 2 by 26 places yields: 134217728
Left-shifting 2 by 27 places yields: 268435456
Left-shifting 2 by 28 places yields: 536870912
Left-shifting 2 by 29 places yields: 1073741824
Left-shifting 2 by 30 places yields: -2147483648
Left-shifting 2 by 31 places yields: 0
4

6 回答 6

3

因为对 s 的算术运算会short产生ints,除非您将它们回退。例如,

short value = 2;
short result = value << 31;

给您一个编译错误,您需要将其转换为short.

这有几个原因,最明显的是

  • 字节码语言并不真正处理任何小于int
  • 否则对shorts 的操作会更频繁地溢出。
  • 更好地对大多数硬件进行建模,这些硬件通常不会对低于 32 位的值进行操作
于 2012-05-29T13:15:40.483 回答
3

来自 Java 语言规范,15.19:“移位运算符”

移位表达式的类型是左侧操作数的提升类型。

提升类型由 5.6 定义:“数字提升”:

否则,如果操作数是编译时类型 byte、short 或 char,则一元数字提升通过扩展转换(第 5.1.2 节)将其提升为 int 类型的值。

于 2012-05-29T13:21:14.670 回答
3

请注意,复合赋值运算符会将结果转换为分配的类型。因此,修改您的示例:

public static void main(String[] args) {
    for (int i = 0; i < 32; i++) {
        short num = 2;
        num <<= i; // equivalent to num = (short)(num << i);
        System.out.println("Left-shifting 2 by " + i + " places yields: " + num);
    }
}

这将产生不同的结果,也许更多是您所期望的。

于 2012-05-29T13:29:08.747 回答
2

30 的意外值是整数溢出,MSB 变为 1 - 因此被插入为负数。

另请注意,运算符 << 正在产生ints (除非参数是 a long,否则它会产生 a long)。(事实上​​,short被扩大到int第一个,然后才被<<评估。)所以结果如预期 - an int,而不是 a short

于 2012-05-29T13:16:13.963 回答
1

正在发生的是操作数的自动提升。在 Java 中,(num << i)您的示例中表达式的结果是类型int。这就是为什么结果在inta 而不是 a的范围内的原因short

如果您尝试分配(num << i)回 a short,则会出现编译错误,迫使您强制转换回 a short

于 2012-05-29T13:19:30.670 回答
1

请参阅SO 线程。

与这个问题相关的部分是这里的引用

If an integer operator has an operand of type long, then the other operand is also
converted to type long. Otherwise the operation is performed on operands of type int, if  
necessary shorter operands are converted into int.

我认为这个想法是这些扩大的转换不会丢失信息,并且可能在溢出的情况下保护您。请参阅Java 语言规范的第 5 章以了解有关扩大和缩小转换的更多信息。

于 2012-05-29T13:30:35.930 回答