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

如何在Jsonnet中用函数式方式实现字符串型有符号整数的加减乘除?

在仅支持64位double的Jsonnet中实现64位有符号大整数基础运算

核心思路

由于Jsonnet仅支持64位double类型,超过2^53的整数无法精确存储,因此采用字符串+安全数字混合处理策略:

  • 对输入字符串先判断是否可安全转为double(数值范围[-2^53, 2^53]),能转则用数字运算,否则保留字符串模拟大整数运算
  • 所有运算函数均为纯函数,避免状态变更,符合Jsonnet函数式特性
  • 运算算法确保时间复杂度为O(m+n)级别(m、n为操作数的位数),避免低效的O(m*n)实现

基础工具函数

1. 安全数字判断与转换

// 判断字符串是否可安全转为double(无精度丢失)
local isSafeNumberStr(s) = (
  local num = std.parseJson(s);
  std.isNumber(num) && std.toString(num) == s
);

// 将输入转为安全数字或保留字符串
local toSafeOperand(x) = (
  if std.isString(x) && isSafeNumberStr(x) then std.parseJson(x)
  else x
);

// 取反操作(处理数字和字符串)
local negate(x) = (
  if std.isNumber(x) then -x
  else if std.isString(x) then (
    if std.startsWith(x, "-") then std.stringSlice(x, 1)
    else "-" + x
  )
  else error "Unsupported operand type"
);

2. 字符串大整数辅助函数

// 移除字符串前导零(处理正数)
local trimLeadingZeros(s) = (
  local idx = std.findSubstr(s, "0", 0);
  if idx == 0 then trimLeadingZeros(std.stringSlice(s, 1))
  else s
);

// 比较两个正数字符串的大小(返回1: a>b, 0:相等, -1:a<b)
local comparePositiveStr(a, b) = (
  if std.length(a) != std.length(b) then std.sign(std.length(a) - std.length(b))
  else (
    if a == b then 0
    else std.sign(std.cmp(a, b))
  )
);

核心运算函数实现

1. 加法(add)

支持数字、字符串混合运算,同号相加、异号转减法:

local add(a, b) = (
  local opA = toSafeOperand(a);
  local opB = toSafeOperand(b);
  
  // 均为安全数字时直接运算
  if std.isNumber(opA) && std.isNumber(opB) then opA + opB
  
  // 混合类型:转字符串处理
  else if std.isNumber(opA) then add(std.toString(opA), opB)
  else if std.isNumber(opB) then add(opA, std.toString(opB))
  
  // 双字符串处理:分正负情况
  else (
    local isNegA = std.startsWith(opA, "-");
    local isNegB = std.startsWith(opB, "-");
    local absA = if isNegA then std.stringSlice(opA, 1) else opA;
    local absB = if isNegB then std.stringSlice(opB, 1) else opB;
    
    // 同号相加
    if isNegA == isNegB then (
      local recAdd = (x, y, carry)::(
        if x == "" && y == "" then (if carry > 0 then std.toString(carry) else "")
        else (
          local digitX = if x == "" then 0 else std.parseInt(std.stringSlice(x, -1));
          local digitY = if y == "" then 0 else std.parseInt(std.stringSlice(y, -1));
          local sum = digitX + digitY + carry;
          recAdd(std.stringSlice(x, 0, -1), std.stringSlice(y, 0, -1), std.floor(sum / 10)) + std.toString(sum % 10)
        )
      );
      local result = trimLeadingZeros(recAdd(absA, absB, 0));
      if isNegA then "-" + result else result
    )
    // 异号:转为减法(a + b = a - (-b))
    else (
      local cmp = comparePositiveStr(absA, absB);
      if cmp == 0 then "0"
      else if cmp > 0 then sub(opA, negate(opB))
      else sub(opB, negate(opA))
    )
  )
);

2. 减法(sub)

基于加法实现,a - b = a + (-b):

local sub(a, b) = add(a, negate(b));

3. 乘法(mult)

采用Karatsuba算法(时间复杂度O(n^1.58),优于O(m*n)的逐位乘法),支持混合类型:

local mult(a, b) = (
  local opA = toSafeOperand(a);
  local opB = toSafeOperand(b);
  
  if std.isNumber(opA) && std.isNumber(opB) then std.floor(opA * opB) // 确保整数结果
  else if std.isNumber(opA) then mult(std.toString(opA), opB)
  else if std.isNumber(opB) then mult(opA, std.toString(opB))
  else (
    local isNegA = std.startsWith(opA, "-");
    local isNegB = std.startsWith(opB, "-");
    local absA = trimLeadingZeros(if isNegA then std.stringSlice(opA, 1) else opA);
    local absB = trimLeadingZeros(if isNegB then std.stringSlice(opB, 1) else opB);
    
    // 基础情况:其中一个为0或1
    if absA == "0" || absB == "0" then "0"
    else if absA == "1" then absB
    else if absB == "1" then absA
    else (
      local lenA = std.length(absA);
      local lenB = std.length(absB);
      local half = std.max(lenA, lenB) // 2;
      
      // 分割字符串为高位和低位
      local splitStr(s, pos) = (
        if std.length(s) <= pos then ("0", s)
        else (std.stringSlice(s, 0, -pos), std.stringSlice(s, -pos))
      );
      local (aHigh, aLow) = splitStr(absA, half);
      local (bHigh, bLow) = splitStr(absB, half);
      
      // Karatsuba核心步骤
      local z0 = mult(aLow, bLow);
      local z1 = mult(add(aLow, aHigh), add(bLow, bHigh));
      local z2 = mult(aHigh, bHigh);
      
      local z1Sub = sub(sub(z1, z2), z0);
      local shiftZ2 = z2 + std.repeat("0", 2*half);
      local shiftZ1 = z1Sub + std.repeat("0", half);
      
      local result = trimLeadingZeros(add(add(shiftZ2, shiftZ1), z0));
      if isNegA != isNegB then "-" + result else result
    )
  )
);

4. 除法(div)

实现整数除法(向零取整),时间复杂度O(m)(m为被除数位数):

local div(a, b) = (
  local opA = toSafeOperand(a);
  local opB = toSafeOperand(b);
  
  if std.isNumber(opA) && std.isNumber(opB) then std.floor(opA / opB) // 向零取整
  else if std.isNumber(opA) then div(std.toString(opA), opB)
  else if std.isNumber(opB) then div(opA, std.toString(opB))
  else (
    local isNegA = std.startsWith(opA, "-");
    local isNegB = std.startsWith(opB, "-");
    local absA = trimLeadingZeros(if isNegA then std.stringSlice(opA, 1) else opA);
    local absB = trimLeadingZeros(if isNegB then std.stringSlice(opB, 1) else opB);
    
    if absB == "0" then error "Division by zero"
    else if comparePositiveStr(absA, absB) < 0 then "0"
    else (
      local recDiv = (dividend, divisor, quotient)::(
        local cmp = comparePositiveStr(dividend, divisor);
        if cmp < 0 then quotient
        else (
          // 快速计算除数左移的位数(避免逐次减)
          local shiftDivisor = (d, count)::(
            if comparePositiveStr(add(d, d), dividend) <= 0 then shiftDivisor(add(d, d), count + 1)
            else (d, count)
          );
          local (shiftedD, shiftCount) = shiftDivisor(divisor, 0);
          recDiv(sub(dividend, shiftedD), divisor, add(quotient, "1" + std.repeat("0", shiftCount)))
        )
      );
      local result = trimLeadingZeros(recDiv(absA, absB, "0"));
      if isNegA != isNegB then "-" + result else result
    )
  )
);

5. 模运算(modulo)

基于除法实现,遵循向零取整后的模规则:

local modulo(a, b) = (
  local opA = toSafeOperand(a);
  local opB = toSafeOperand(b);
  
  if std.isNumber(opA) && std.isNumber(opB) then opA - (std.floor(opA / opB) * opB)
  else sub(opA, mult(div(opA, opB), opB))
);

比较操作实现

local eq(a, b) = (
  local opA = toSafeOperand(a);
  local opB = toSafeOperand(b);
  if std.isNumber(opA) && std.isNumber(opB) then opA == opB
  else if std.isString(opA) && std.isString(opB) then opA == opB
  else eq(std.toString(opA), std.toString(opB))
);

local gt(a, b) = (
  local opA = toSafeOperand(a);
  local opB = toSafeOperand(b);
  if std.isNumber(opA) && std.isNumber(opB) then opA > opB
  else if std.isString(opA) && std.isString(opB) then (
    local isNegA = std.startsWith(opA, "-");
    local isNegB = std.startsWith(opB, "-");
    if isNegA != isNegB then !isNegA
    else (
      local cmp = comparePositiveStr(
        if isNegA then std.stringSlice(opA, 1) else opA,
        if isNegB then std.stringSlice(opB, 1) else opB
      );
      if isNegA then cmp < 0 else cmp > 0
    )
  )
  else gt(std.toString(opA), std.toString(opB))
);

// 其余比较操作(lt, ge, le)可基于gt/eq实现
local lt(a, b) = !ge(a, b);
local ge(a, b) = gt(a, b) || eq(a, b);
local le(a, b) = lt(a, b) || eq(a, b);

使用示例

// 安全数字运算
add(123, 456) // 输出579
mult(999, 999) // 输出998001

// 大整数运算
add("9223372036854775807", "1") // 输出"9223372036854775808"(超过2^53,用字符串存储)
div("1234567890123456789", "123") // 输出"10037137318076884"
modulo("-12345", "7") // 输出"-4"(向零取整规则)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 23:42:44