You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript与Rust跨平台位移运算结果不一致问题排查

问题概述

我用Rust实现了一个decompose函数,对i64类型进行位移操作(输入非负),同时用TypeScript基于bigint实现了相同逻辑,但输入整数844562369609728时两段代码结果不一致,TypeScript返回错误结果。按AssemblyScript文档,i64对应JS的bigint,理论上应该兼容,请问原因是什么?

Rust代码:

pub const fn decompose(result: i64) -> (bool, u64, u64) {
    let ptr = (result >> 32) as u64;
    let len = ((result << 32) >> 48) as u64;
    let success = ((result << 63) >> 63) == 0;

    (success, ptr, len)
}

TypeScript代码:

interface CallResult {
  ptr: bigint;
  len: bigint;
  status: boolean;
}

function decompose(result: bigint): CallResult {
  let ptr = result >> 32n;
  let len = (result << 32n) >> 48n;
  let success = (result << 63n) >> 63n == 0n;

  return {
    ptr: ptr,
    len: len,
    status: success,
  };
}
核心原因:整数精度与溢出行为差异

两段代码的本质差异在于整数的存储范围和溢出处理逻辑:

  • Rust的i64是固定64位的有符号整数,所有位移操作都会自动截断到64位范围(溢出时仅保留低64位),且位移遵循有符号整数的算术规则。
  • TypeScript的bigint是任意精度整数,不会自动截断,位移操作会完整保留所有高位数据,不存在溢出截断行为。

以输入844562369609728(即3 * 2^48)为例:

  1. len计算差异:
    • Rust中:result << 32会将3 * 2^48左移32位得到3 * 2^80,超出i64的最大值(2^63 - 1),因此自动截断为低64位(全0),再右移48位后结果仍为0。
    • TypeScript中:bigint会完整保留3 * 2^80,右移48位后得到3 * 2^32,与Rust结果完全不同。
  2. success计算差异:
    • Rust中:result << 63得到3 * 2^111,截断到64位后为0,算术右移63位仍为0,因此success为true。
    • TypeScript中:result << 63n完整保留3 * 2^111,右移63位后得到原输入值3 * 2^48,不等于0n,因此success为false。
解决方案:在TypeScript中模拟Rust的i64行为

要让TypeScript代码与Rust逻辑一致,需要手动模拟i64的64位截断和有符号位移规则:

interface CallResult {
  ptr: bigint;
  len: bigint;
  status: boolean;
}

function decompose(result: bigint): CallResult {
  const I64_MAX = 0x7FFFFFFFFFFFFFFFn;
  const I64_MASK = 0xFFFFFFFFFFFFFFFFn;
  const U32_MASK = 0xFFFFFFFFn;
  const U16_MASK = 0xFFFFn;

  // 将输入截断为i64范围的有符号bigint
  let truncated = result & I64_MASK;
  if (truncated > I64_MAX) {
    truncated -= 0x10000000000000000n;
  }

  // 计算ptr:模拟i64右移32位后转u64
  let ptr = truncated >> 32n;
  ptr = ptr < 0n ? ptr + U32_MASK + 1n : ptr;

  // 计算len:模拟i64左移32位截断后,再右移48位转u64
  let shiftedLeft = (truncated << 32n) & I64_MASK;
  shiftedLeft = shiftedLeft > I64_MAX ? shiftedLeft - 0x10000000000000000n : shiftedLeft;
  let len = shiftedLeft >> 48n;
  len = len < 0n ? len + U16_MASK + 1n : len;

  // 计算success:模拟i64左移63位截断后右移63位
  let shiftedLeft63 = (truncated << 63n) & I64_MASK;
  shiftedLeft63 = shiftedLeft63 > I64_MAX ? shiftedLeft63 - 0x10000000000000000n : shiftedLeft63;
  let success = shiftedLeft63 >> 63n === 0n;

  return { ptr, len, status: success };
}

这段代码通过手动截断到64位、处理有符号数的符号位,完全复刻了Rust中i64的位移和类型转换逻辑,与原Rust代码结果一致。

内容的提问来源于stack exchange,提问作者weegee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 01:03:12