【问题标题】:Javascript - parse string to longJavascript - 将字符串解析为长
【发布时间】:2018-03-08 22:30:24
【问题描述】:

我在python中有一个工作脚本,使用long(16)根据指定的基数将字符串转换为整数:

modulus=public_key["n"]    
modulusDecoded = long(public_key["n"], 16)

打印: 8079d7ae567dd2c02dadd1068843136314fa3893fa1fb1ab331682c6a85cad62b208d66c9974bbbb15d52676fd9907efb158c284e96f5c7a4914fd927b7326c40efa14922c68402d05ff53b0e4ccda90bbee5e6c473613e836e2c79da1072e366d0d50933327e77651b6984ddbac1fdecf1fd8fa17e0f0646af662a8065bd873

90218878289834622370514047239437874345637539049004160177768047103383444023879266805615186962965710608753937825108429415800005684101842952518531920633990402573136677611127418094912644368840442620417414685225340199872975797295511475162170060618806831021437109054760851445152320452665575790602072479287289305203

分别。 这看起来像是十六进制到十进制的转换。 我试图在 JS 中得到相同的结果,但 parseInt()parseFloat() 产生了完全不同的结果。除此之外,JavaScript 似乎不喜欢输入字符串中的字符,有时会返回 NaN。

谁能提供一个函数/指导如何获得与 Python 脚本中相同的功能?

【问题讨论】:

  • 我认为这很接近,但它返回浮点...当然,在发布问题后,我在这里找到了几乎准备好的解决方案:(danvk.org/hex2dec.html)
  • 尝试将字符串转换为数字的目的是什么?为什么不使用数字的字符串表示?
  • @guest271314 我认为 OP 没有完全了解 js 数字精度...
  • @Jonasw 是的,你是对的。我的主要问题是理解精度和溢出。

标签: javascript python cryptography


【解决方案1】:

JavaScript 中的数字是浮点数,因此它们总是在某个数字之后失去精度。要拥有无限的数字,我们宁愿使用从 0 到 9 的数字数组,它的范围是无限的。为此,基于十六进制字符串输入,我进行十六进制到整数数组的转换,然后我使用double dabble algorithm 将数组转换为 BCD。可以轻松打印:

const hexToArray = arr => arr.split("").map(n => parseInt(n,16));


const doubleDabble = arr => {
  var l = arr.length;
  for( var b = l * 4; b--;){

    //add && leftshift
    const overflow = arr.reduceRight((carry,n,i) => {

      //apply the >4 +3, then leftshift
      var shifted = ((i < (arr.length - l ) && n>4)?n+3:n ) << 1;

      //just take the right four bits and add the eventual carry value
      arr[i] = (shifted & 0b1111) | carry;

      //carry on
      return shifted > 0b1111;
    }, 0);
    // we've exceeded the current array, lets extend it:
    if(overflow) arr.unshift(overflow);
  }
  return arr.slice(0,-l);
};

const arr = hexToArray("8079d7");
const result = doubleDabble(arr);      
console.log(result.join(""));

Try it

【讨论】:

  • 虽然 TheChetan 的回答也非常翔实,但这个想法与实际帮助我的想法相似:danvk.org/hex2dec.html
  • @darth0s 是的,我会用我的赏金来奖励它。虽然使用数字数组的基本思想是相似的,但算法本身是完全不同的,希望比你找到的算法更快
【解决方案2】:

使用内置的 api parseInt,您可以在 Firefox 上获得高达 100 位的准确度,在 Chrome 上获得 20 位的准确度。

a = parseInt('8079d7ae567dd2c02dadd1068843136314fa3893fa1fb1ab331682c6a85cad62b208d66c9974bbbb15d52676fd9907efb158c284e96f5c7a4914fd927b7326c40efa14922c68402d05ff53b0e4ccda90bbee5e6c473613e836e2c79da1072e366d0d50933327e77651b6984ddbac1fdecf1fd8fa17e0f0646af662a8065bd873', 16)

a.toPrecision(110)
> Uncaught RangeError: toPrecision() argument must be between 1 and 21

# Chrome
a.toPrecision(20)
"9.0218878289834615508e+307"  

# Firefox
a.toPrecision(100)
"9.021887828983461550807409292694387726882781812072572899692574101215517323445643340153182035092932819e+307"

来自ECMAScript Spec

  1. 设 p 是 ? ToInteger(精度)。
    ...
  2. 如果 p 100,则抛出 RangeError 异常。

【讨论】:

  • 我相信这应该被接受为一个答案,因为它指的是一些简单的事实:1)JS 数字是浮点数,无论如何; 2) 存在浏览器强加的范围和精度限制。由此得出的结论是,对于真正的大数字,您需要一种特殊的数据类型来保存它们,而 number 在一般情况下根本不可能工作。
  • @igor 但这错过了一步:虽然我认为将 int 表示为数组或字符串是唯一可能的方法,但 100 位数字可能不够准确,此处不要求使用数学符号所以少了一步
  • 这个答案具有误导性,JavaScript 在设计上(通过数字的表示方式)只允许大约 15 位精度。 Firefox 示例仅精确到前 15 位数字:9.02188782898346...,之后正确的数字序列是 ...223705...,而不是 Firefox 和 Chrome 给出的 ...155080...。不要依赖数字来准确表示比它们设计的精度更高的精度(根据 IEEE 754 标准)。
【解决方案3】:

this answer 中所述,JavaScript 数字不能表示大于9.007199254740991e+15 的整数而不损失精度。

在 JavaScript 中处理较大的整数需要 BigInt 库或其他特殊用途的代码,然后通常将大整数表示为字符串或数组。

重用来自this answer 的代码有助于转换十六进制数字表示

8079d7ae567dd2c02dadd1068843136314fa3893fa1fb1ab331682c6a85cad62b208d66c9974bbbb15d52676fd9907efb158c284e96f5c7a4914fd927b7326c40efa14922c68402d05ff53b0e4ccda90bbee5e6c473613e836e2c79da1072e366d0d50933327e77651b6984ddbac1fdecf1fd8fa17e0f0646af662a8065bd873 P>

到它的十进制表示

90218878289834622370514047239437874345637539049004160177768047103383444023879266805615186962965710608753937825108429415800005684101842952518531920633990402573136677611127418094912644368840442620417414685225340199872975797295511475162170060618806831021437109054760851445152320452665575790602072479287289305203 P>

如以下sn-p所示:

function parseBigInt(bigint, base) {
  //convert bigint string to array of digit values
  for (var values = [], i = 0; i < bigint.length; i++) {
    values[i] = parseInt(bigint.charAt(i), base);
  }
  return values;
}

function formatBigInt(values, base) {
  //convert array of digit values to bigint string
  for (var bigint = '', i = 0; i < values.length; i++) {
    bigint += values[i].toString(base);
  }
  return bigint;
}

function convertBase(bigint, inputBase, outputBase) {
  //takes a bigint string and converts to different base
  var inputValues = parseBigInt(bigint, inputBase),
    outputValues = [], //output array, little-endian/lsd order
    remainder,
    len = inputValues.length,
    pos = 0,
    i;
  while (pos < len) { //while digits left in input array
    remainder = 0; //set remainder to 0
    for (i = pos; i < len; i++) {
      //long integer division of input values divided by output base
      //remainder is added to output array
      remainder = inputValues[i] + remainder * inputBase;
      inputValues[i] = Math.floor(remainder / outputBase);
      remainder -= inputValues[i] * outputBase;
      if (inputValues[i] == 0 && i == pos) {
        pos++;
      }
    }
    outputValues.push(remainder);
  }
  outputValues.reverse(); //transform to big-endian/msd order
  return formatBigInt(outputValues, outputBase);
}

var largeNumber =
'8079d7ae567dd2c02dadd1068843136314fa389'+
'3fa1fb1ab331682c6a85cad62b208d66c9974bb'+
'bb15d52676fd9907efb158c284e96f5c7a4914f'+
'd927b7326c40efa14922c68402d05ff53b0e4cc'+
'da90bbee5e6c473613e836e2c79da1072e366d0'+
'd50933327e77651b6984ddbac1fdecf1fd8fa17'+
'e0f0646af662a8065bd873';

//convert largeNumber from base 16 to base 10
var largeIntDecimal = convertBase(largeNumber, 16, 10);

//show decimal result in console:
console.log(largeIntDecimal);

//check that it matches the expected output:
console.log('Matches expected:',
  largeIntDecimal === '90218878289834622370514047239437874345637539049'+
    '0041601777680471033834440238792668056151869629657106087539378251084294158000056'+
    '8410184295251853192063399040257313667761112741809491264436884044262041741468522'+
    '5340199872975797295511475162170060618806831021437109054760851445152320452665575'+
    '790602072479287289305203'
);

//check that conversion and back-conversion results in the original number
console.log('Converts back:',
  convertBase(convertBase(largeNumber, 16, 10), 10, 16) === largeNumber
);

【讨论】:

    猜你喜欢
    • 2019-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多