美文网首页
在进行Long变量的位运算时的注意事项

在进行Long变量的位运算时的注意事项

作者: Omega_Ariston | 来源:发表于2017-12-20 15:29 被阅读34次

    最近在写一个棋类游戏,8×8的棋盘。为了更高的运算效率,我选择了使用一个64位的基础数据类型long变量,名字叫board,作为二进制位图来存储棋盘的状态,但也因此碰上了一个颇为有趣的问题。
    事情经过是这样的,这个board初始值为0,表示棋盘上没有棋子,假设我在第m行第n列下了一颗棋子,则board的低位数起第(m*8+n)位会被设置为1,表示该处有一枚棋子。
    这个过程我的代码是这么写的:

    board |= 1<<(m*8+n);
    

    乍一眼看上去好像没什么问题。
    然而程序最终运行时出现了些诡异且匪夷所思的bug,导致我不得不逐行检查代码,最后发现问题就出现在上面这一行中。

    其实当初多看两眼就能发现问题了呢。

    问题出在等号右边的那个1上面,Java中整型默认为int,浮点数默认为double。这个1在这里就是一个32位的int。
    运算结果会因此出现两种错误:

    1. 因为int总共只有32位,因此大于等于32位的位移操作会先将位数与32取余再进行移动,比如说 (k<<32) ,实际上是 (k<<(32%32)), 也就是 (k<<0),也就是k原本的值。所以这里如果 (m*8+n)的值大于等于32,那这颗本该下到高32位上的棋子就会下到低32位的某一位上。
    2. 由于等号左边的board是long变量,而等号右边的值为int类型,这个过程中int类型会被强制向long类型转换。如果这个int在十进制下是一个非负数(符号位为0),那可以直接高位补32个0进行转换,但如果这个int在十进制下是一个负数(符号位为1),那么转换后的long为了保持十进制数值不变,这个long会将高32位全部补1。
      觉得不太好想的话,我就拿8位的byte转16位的short来举个例子(这样可以少打点0)
    byte a = -128    //  二进制形式为10000000
    short b = a        //二进制形式为11111111 10000000
    //我们来计算b的相反数:
    //先取反码
    //00000000 01111111
    //加上
    //00000000 00000001
    //等于
    //00000000 10000000
    //计算出b的相反数为128
    //故b = a = -128
    

    所以,如果我在下棋的时候,刚好下到(m*8+n)=31这个位置时,board变量的前32位就会被强制设置为1,这并不是我们想看到的。
    而解决这两个问题的方法也非常简单,只需要保证等号右边的整型也是64位long类型即可。修改后的代码如下:

    board |= 1L<<(m*8+n);    //1后面多加了一个L哦
    

    简单吧!

    相关文章

      网友评论

          本文标题:在进行Long变量的位运算时的注意事项

          本文链接:https://www.haomeiwen.com/subject/yeugwxtx.html