当前位置: 首页 > 原理解释

java移位运算原理-Java 移位运算原理

我是你的 Java 应试高手,专治各种“想拿高分却绕弯子”的死题。别整那些虚头巴脑的开场白,直接上干货。移位运算,说白了就是把二进制货郎担从主叫方甩到被叫方,要么反过来。别被 `<<` 后面那个 crazy 的符号吓到,它跟赋值号 `=` 长得像,没啥本质区别,就是个算盘珠子往右侧拨一拨的事儿。 在二进制世界里,移位不是好办的“复制”,而是“搬橡皮泥”。
看那 `1100` 这个数,左边那个 `1` 是个开关,关得上,也能关掉。把它往右移一位,相当于把原来的 `1` 扔到了高几进位,剩下的高位的 `0` 自动填补空缺。
这就好比把一串电话号码向右拨,后面的空位就是 0,原来的前一位数字自动爬上去填补空缺。 这就解释了为啥 `>>` 总归是“无符号右移”,出于机器一直把高位变成 0;而 `>>>` 才是“有符号右移”,出于不管前面是啥,高位一辈子是 0,它强迫机器把数字“裁剪”下来,只留正数局部。
反过来看 `<<`,就是向左搬,把低位的 `0` 挤到高位去,配合自然溢出的 `1`,新的高位自然就长出来了。
这就叫“补零”,然后是“冒号”。 举个具体的例子,把 `0x20` 搬一搬去。先拆解一下,`0x20` 实际上等于十进制的 `32`。二进制写法是 `00100000`。
这时候能够看到,最高位(第 6 位)是 `0`。
要是按“补零右移”搞,结局就得变成 `00010000`,也就是 `16`。
要是你按“冒号左移”搞,那就是把 `1` 弹到第 5 位,结局变成 `10000000`,也就是 `128`。
这差别可就大了。 这就引出了移位运算最核心的坑——溢出。左移的时候,你往左边挤,高进位的 `1` 就得吐出来。吐出来的 `1` 去哪了?它进到了高位的进位寄存器里。
比如 `1000` 左移一位,`000` 被挤出去了,结局变成了 `10000`,这就是自然溢出。自然溢出和截断溢出是两码事,自然溢出的 `1` 要补到高位,而截断溢出只要去掉溢出位就行。 再说说优先级,移位算符的优先级实际上挺高的,得比比较运算符 `>`、`<` 要高,就连比 `+`、`-` 还要高。你得先算移位,再算加减。
比如 `2 + 1 << 3`,这里的 `1 << 3` 先算了,等于 `8`。
然后再算 `2 + 8`,结局是 `10`。
要是顺序反了,比如 `2 + 1 <<` 写成 `2 + 1 << 3`,编译器会直接给你算好,给你个 10。你要是写成 `2 + 1` 再移位,那结局就是 `3` 搬位,等于 `24`。
记住这个:运算符结合性,从左往右,先乘除,再移位,最终加减。 还有掩码移位。移位运算是四元运算,就像 `A + B + C + D` 一样。左边是你要搬的数,右边是你搬的位置。
要是右边是负数,比如 `-1`,在二进制里实际上是个全 `1` 的数,也就是 `...1111`。
这时候 `-1 << 1` 就不是搬位了,而是做异或运算,把位都翻面。
这就得小心了,别把负数当成正数用。 散列函数里常用移位算子。
比如 `x = x 32`,实际上就是 `x = x << 5`。再比如 `x = (x - base) 2`,就是 `(x - base) << 1`。但要注意,一般/平平整数右移要是是负数,可能会出错。
比如 `Integer.MIN_VALUE` 右移一位,结局变成 `Integer.MIN_VALUE >> 1`,这会害得毛病。
这时候得用 `x >>> 1` 要么 `x & 0x7FFFFFFF` 来强制处理成无符号移动。 最终说几句实战心得。别死记硬背移位算符的优先级,那是过时的习惯。目前的编译器挺智慧,它们会根据上下文自己判断。
看代码时,看表达式结构,看有没有嵌套,别去纠结顺序。移位运算主要用在位掩码操作、循环移位、还有某些特殊的加密算法里。平时写代码,要不就涉及到位操作要么把负数当正数用,否则极少单独用移位运算。 总而言之,位移就是移动。左移是向前搬,多吃点进位;右移是向后搬,回绝进位,最好补零。代码里灵活运用,比背公式关键多了。遇到负数移位,记得加个 `>>>` 要么按无符号算。
这就是Java 里最实用、也最坑的移位技巧。
相关标签:

猜你喜欢

热门阅读

  • 赖柴尔定理-赖柴尔定理
  • 迪拜哪个国家的城市?-迪拜在哪国城市
  • 李毅吧番号及出处-李毅吧番号及出处
  • 贴春联的由来简介50字-春联由来简述
  • 思乡的名言和出处-思乡名言及出处

其他分站