Javascript - синтаксический анализ длинной строки

У меня есть рабочий script в python, который выполняет преобразование целых чисел в целое число, основанное на указанном радиусе, используя long(16):

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

который печатает: 8079d7ae567dd2c02dadd1068843136314fa3893fa1fb1ab331682c6a85cad62b208d66c9974bbbb15d52676fd9907efb158c284e96f5c7a4914fd927b7326c40efa14922c68402d05ff53b0e4ccda90bbee5e6c473613e836e2c79da1072e366d0d50933327e77651b6984ddbac1fdecf1fd8fa17e0f0646af662a8065bd873

и 90218878289834622370514047239437874345637539049004160177768047103383444023879266805615186962965710608753937825108429415800005684101842952518531920633990402573136677611127418094912644368840442620417414685225340199872975797295511475162170060618806831021437109054760851445152320452665575790602072479287289305203

соответственно. Это выглядит как переход от шестнадцатеричного к десятичному. Я попытался получить тот же результат в JS, но parseInt() и parseFloat() произвел нечто совершенно другое. Вдобавок к этому JavaScript, похоже, не нравится символам в строке ввода и иногда возвращает NaN.

Может ли кто-нибудь указать функцию/руководство, как получить ту же функциональность, что и в Python script?

Ответ 1

Числа в JavaScript - это плавающие точки, поэтому они всегда теряют точность после определенной цифры. Чтобы иметь неограниченные числа, можно использовать массив чисел от 0 до 9, который имеет неограниченный диапазон. Чтобы сделать это на основе ввода шестнадцатеричной строки, я делаю преобразование hex-массива в int, тогда я использую двойной алгоритм dabble для преобразования массива к 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(""));

Попробуйте

Ответ 2

Используя встроенный api parseInt, вы можете получить до 100 цифр точности в Firefox и 20 цифр точности в Chrome.

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 < 1 или p > 100, выведите исключение RangeError.

Ответ 3

Как описано в этом ответе, номера JavaScript не могут представлять целые числа, превышающие 9.007199254740991e + 15 без потерь точности.

Работа с большими целыми числами в JavaScript требует библиотеки BigInt или другого специального кода, а большие целые числа обычно будут представлены как строки или массивы.

Повторное использование кода из этого > ответа помогает преобразовать представление шестнадцатеричного числа

8079d7ae567dd2c02dadd1068843136314fa3893fa1fb1ab331682c6a85cad62b208d66c9974bbbb15d52676fd9907efb158c284e96f5c7a4914fd927b7326c40efa14922c68402d05ff53b0e4ccda90bbee5e6c473613e836e2c79da1072e366d0d50933327e77651b6984ddbac1fdecf1fd8fa17e0f0646af662a8065bd873

к его десятичному представлению

90218878289834622370514047239437874345637539049004160177768047103383444023879266805615186962965710608753937825108429415800005684101842952518531920633990402573136677611127418094912644368840442620417414685225340199872975797295511475162170060618806831021437109054760851445152320452665575790602072479287289305203

как показано в следующем фрагменте:

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
);