C#与JavaScript位运算结果不一致问题(含Long库使用疑问)
C#与JavaScript位运算结果不一致的原因及解决方法
问题背景
C#原始代码
uint HASHTABLE = 1917507215; for (uint i = 5; i != 0; i--) { HASHTABLE = (HASHTABLE ^ ((HASHTABLE << 10 ^ HASHTABLE) << 9 ^ HASHTABLE) << 5) & 0x7fffffff ^ (((HASHTABLE << 7 ^ HASHTABLE) << 10 ^ HASHTABLE) << 9 ^ HASHTABLE) << 5; HASHTABLE = HASHTABLE >> 1 | (HASHTABLE & 1) << 0x1f; }
C#循环结束后,HASHTABLE的结果为1871861428(0x6F9256B4)。
JavaScript尝试过程
- 原生JS因仅支持32位有符号整数,运算结果为**-275622220**,与C#结果不符。
- 改用Long库模拟无符号整数运算,代码如下:
hashTable = Long.fromValue(hashTable, true); for (let i = 5; i != 0; i--) { hashTable = hashTable.xor( hashTable.shiftLeft(10).xor(hashTable).shiftLeft(9).xor(hashTable).shiftLeft(5) ).and(0x7fffffff).xor( hashTable.shiftLeft(7).xor(hashTable).shiftLeft(10).xor(hashTable).shiftLeft(9).xor(hashTable).shiftLeft(5) ); hashTable = hashTable.shiftRight(1).or(hashTable.and(1).shiftLeft(0x1f)); } hashTable = hashTable.toInt();
但结果为4019345076(0xEF9256B4),与C#结果的差异是第32位(最高有效位)被错误置位。
不一致原因
- 位宽差异:C#的
uint是32位无符号整数,所有位运算会自动截断到32位;Long库默认处理64位整数,移位等操作不会自动截断,导致高位残留无效数据,干扰最终结果。 - 右移逻辑错误:C#中
uint的>>是逻辑右移(高位补0),但Long库的shiftRight是算术右移(高位补符号位),这会导致循环右移步骤的高位计算错误。 - 中间结果未截断:原Long库代码中,移位、异或的中间结果没有强制保留低32位,64位的高位数据参与后续运算,最终导致第32位被错误置位。
解决方法
修改Long库代码,模拟C#的32位无符号整数运算逻辑,每一步运算后截断到32位,同时修正右移逻辑:
let hashTable = Long.fromValue(1917507215, true); for (let i = 5; i !== 0; i--) { // 计算第一部分:(HASHTABLE ^ ((HASHTABLE <<10 ^ HASHTABLE) <<9 ^ HASHTABLE) <<5) & 0x7fffffff let part1 = hashTable.shiftLeft(10).xor(hashTable).and(0xFFFFFFFF) .shiftLeft(9).xor(hashTable).and(0xFFFFFFFF) .shiftLeft(5).and(0xFFFFFFFF); part1 = hashTable.xor(part1).and(0x7fffffff); // 计算第二部分:(((HASHTABLE <<7 ^ HASHTABLE) <<10 ^ HASHTABLE) <<9 ^ HASHTABLE) <<5 let part2 = hashTable.shiftLeft(7).xor(hashTable).and(0xFFFFFFFF) .shiftLeft(10).xor(hashTable).and(0xFFFFFFFF) .shiftLeft(9).xor(hashTable).and(0xFFFFFFFF) .shiftLeft(5).and(0xFFFFFFFF); hashTable = part1.xor(part2).and(0xFFFFFFFF); // 模拟C#无符号循环右移:HASHTABLE >>1 | (HASHTABLE &1) <<0x1f let lsb = hashTable.and(1); hashTable = hashTable.shiftRightUnsigned(1).or(lsb.shiftLeft(0x1f)).and(0xFFFFFFFF); } // 转换为32位无符号整数结果 const result = hashTable.toNumber(); console.log(result); // 输出1871861428,与C#结果一致
关键修改点:
- 每一步运算后用
.and(0xFFFFFFFF)截断到32位,避免64位高位数据干扰。 - 用
shiftRightUnsigned(1)替代shiftRight(1),模拟C#无符号整数的逻辑右移。 - 将复杂运算拆分为
part1和part2,便于分步截断和调试。
内容的提问来源于stack exchange,提问作者Wilhelm Sorban
相关产品推荐
相关产品推荐

