如何不用库解决 py 位移运算结果和 js 不同

        相信各位在去将 javaScript 的代码转换成 python 都会遇到一个问题,就是这两个语言在某些数字上的位运算结果不同。

        首先我们要明白他们两个处理结果不同的原因,经过查询资料我发现,在 py 中数字类型他是可以无限叠加的,也就是它有无数位,和 javaScript 不同, javaScript 的数字类型有很多,而 javaScript 进行位运算的数字类型大多数情况下都是 int 类型,而这个 int 类型他的长度只有64位。这就会导致两个语言进行位运算时结果会大不相同,例如:

        可以看出两个语言对 1000 左移 25 位的结果是完全不同的

        想要使 py 与 js 的位运算结果相同,那么我们得先知道这两个语言在进行位运算都做了些什么,接下来有请计算器,我们先将上面两个值放在计算器中去查看他们的二进制码有什么不同

可以看到 -805306368(将其称作A) 的二进制为:

1111 1111 1111 1111 1111 1111 1111 1111 1101 0000 0000 0000 0000 0000 0000 0000

33554432000 (将其称作B)的二进制为:

0111 1101 0000 0000 0000 0000 0000 0000 0000

然后我们可以看到 A 的长度为 64 位,B 的长度却只有 36 位,整整少了 28 位,我们对比一下他们二进制有什么不同

A 1111 1111 1111 1111 1111 1111 1111 1111 1101 0000 0000 0000 0000 0000 0000 0000

B                                                          0111 1101 0000 0000 0000 0000 0000 0000 0000

可以清楚的看到从第 35 位一直到第 1 位他们都是一样的,但是到第36位时 B 为 0 而 A 却为 1,按照正常来说 js 应该和 py 生成的相同,毕竟 1000 左移 25 位后还没有超过 64 位,结果应该相同,然后我就去查阅了一些资料,看到这么一句话:”js 的数字类型做位移运算时是根据 32 位来进行的,但他的输出却是按照 64 位输出“,哎这就和我们前面所说的一样,js 确实是 64 位,但操作时就会变成 32 位,这就有点坑人了,那我们就顺着这个思路来去推断 js 1000 << 25 是做了些什么

首先 先将 1000 转为二进制

                                                             0011 1110 1000

然后将其左移 25  位

        0111 1101 0000 0000 0000 0000 0000 0000 0000

然后就可以看到此时的结果和 py 的是一模一样的,但是我们先前说了, js 位移运算时时根据 32 位来的,那么我们就需要砍掉多余的部分,也就是红色部分

      C  0111 1101 0000 0000 0000 0000 0000 0000 0000  

就可以得到这样一个 32 位的二进制,然后我们去和 A 去对比一下

                 1101 0000 0000 0000 0000 0000 0000 0000

1111 1111 1111 1111 1111 1111 1111 1111 1101 0000 0000 0000 0000 0000 0000 0000

可以看出 剩下的 32 位补的都是 1,在什么情况下它会补 1 呢?我们看下面这个图

这张图是我在 js 中运行 1000 << 21 位后得到的数字进行二进制转换后得到的结果,可以看到它的左边的 32 位并没有去补1 ,那我们是不是可以推断出,js 在进行位移运算是 最高位为什么,那么剩下的 32 位就会补什么,1000 << 21 的最高位为 0,那么剩下的就全部补上 0,就会如同上面一样为 0111 1101 0000 0000 0000 0000 0000 0000,而我们之前 1000 << 25 位后截取 32 位后得到的C 它的最高位为 1,然后它的最左边就全部补上 1 就会得到 A

那么是不是我们所说的那样?我们来验证一下先在 py 中将 1000 << 24 得到 16777216000,然后我们将他转换为二进制

0011 1110 1000 0000 0000 0000 0000 0000 0000

然后将多余的 4 位删掉

1110 1000 0000 0000 0000 0000 0000 0000

最高位为 1 ,那么左边的 32 位就都补上 1

1111 1111 1111 1111 1111 1111 1111 1111 1110 1000 0000 0000 0000 0000 0000 0000

我们将其放在计算器中转换一下,可以就可以得到 -402,653,184

我们去 js 上运行 1000 << 24 看看是否和我们自己生成出来的值相同

可以看出和 js 生成出来的完全相同的,那么我们就可以在 py 中实现和 js 位移运算相同的结果

首先我们需要一个前置条件,也就是 32 位二进制的最高位为 1 如下

1000 0000 0000 0000 0000 0000 0000 0000 ---》 2147483648

想要最高位始终为 1 的话,那就是要大于等于这个数字所以当我们 py 左移生成的数字大于等于 2147483648  的时候最高位就为 1

我们得到这个前置条件后就需要对他进行一个 | 运算

首先 1000 << 25

0011 1110 1000 0000 0000 0000 0000 0000 0000

想要保留 32 位的所有数字,且 33-64 位的数字都为 1 就需要进行 | 运算,因为 | 的话是 1 0 1 1 都为 1,0 0 就为0,那么我们只需要一个 32 位都为 0 的二进制就可以将其 32 位匹配出来,那么剩下的 33-64 都为 1 就可以将 33-64 位都转换成 1,如下

0000 0000 0000 0000 0000 0000 0000 0011 1110 1000 0000 0000 0000 0000 0000 0000

| | | |    | | | |  | | | |  | | | |  | | | |  | | | |  | | | |  | | | |  | | | |  | | | |  | | | |   | | | |  | | | |  | | | |  | | | |   | | | |

1111   1111  1111  1111 1111  1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000

进行了 | 运算后就可以得到

11111   1111  1111  1111 1111  1111 1111 1111 1101 0000 0000 0000 0000 0000 0000 0000

转换为 10 进制就是 -805306368,接下来我们用 py 实现

def js_left(x, y):
    x <<= y
    if x >= 2147483648:
        x |= -4294967296
    return x


print(js_left(1000, 25)) # -805306368

我们可以看到现在我们生成的结果已经和 js 的一模一样了,我就不在这里测试了,我自己测了基本都是正确的,结束了吗?并没有,正数搞完了还有负数,负数的逻辑和正数的一样,我就简单的带着过一下。

依然是我们的老熟人 1000,可以看到前面我们虽然改写了左移使正数的左移和 js 的相同了,但改成负数就不同了,废话不多说,直接上计算器


前面认真看了的小伙伴,就知道 py 生成就是没有任何修改的左移,js 的就是有一些小小的修改

js 

0011 0000 0000 0000 0000 0000 0000 0000

py

1111 1111 1111 1111 1111 1111 1111 1000 0011 0000 0000 0000 0000 0000 0000 0000

-1000

1111 1111 1111 1111 1111 11111111  1111  1111  1111 1111  1111  1111 1100 0001 1000

首先得明白要满足什么样前置条件的情况下,只要 32位 的二进制

就是左移后的数字小于等于  -2147483648 时,只要 32位的二进制

-2147483648:

1111 1111 1111 1111 1111 1111 1111 1111 1000 0000 0000 0000 0000 0000 0000 0000

为啥呢?因为只要这个数字小于等于 -2147483648 时,32进制的最高位才为 0,也就是可以直接进行切割的情况,例如

-2147483649

1111 1111 1111 1111 1111 1111 1111 1111 0111 1111 1111 1111 1111 1111 1111 1111

-2147483649 << 0

刚好最高位就是为 0 就可以直接进行切割

想要切割的话,就是将33-64位全部变为0,0-32,保持不变,这个就要用到 & 运算,他的性质是 1 1 为 1, 1 0,0 0,为0

根据这个性质,我们只需要将 33-64,变为0, 0-32变为1就好,就可以得到

4294967295

0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 1111 1111

然后将 -2147483649 和 4294967295 进行 & 运算得到

2147483647

0111 1111 1111 1111 1111 1111 1111 1111

然后将我们这个推断和上面的进行结合,就可以得到如下代码

def js_left(x, y):
    x = x << y
    if x & 2147483648:
        x |= -4294967296
    else:
        x &= 4294967295
    return x


print(js_left(1000, 25)) 
print(js_left(-2147483649, 0))

至此完美解决了如何不用库就可以将 py 和 js 的位移运算保持相同

重新改过了,我在上面搞错了一个事,就是大于 2147483648 这个数不一定代表第三十二位为 1 ,所以就导致有些数字位移出来的结果有问题,用 x & 2147483648 就可以判断第三十二位是 1 还是 0 ,就可以根据他们不同进行切割

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值