12

I have a working script in python doing string to integer conversion based on specified radix using long(16):

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

which prints: 8079d7ae567dd2c02dadd1068843136314fa3893fa1fb1ab331682c6a85cad62b208d66c9974bbbb15d52676fd9907efb158c284e96f5c7a4914fd927b7326c40efa14922c68402d05ff53b0e4ccda90bbee5e6c473613e836e2c79da1072e366d0d50933327e77651b6984ddbac1fdecf1fd8fa17e0f0646af662a8065bd873

and 90218878289834622370514047239437874345637539049004160177768047103383444023879266805615186962965710608753937825108429415800005684101842952518531920633990402573136677611127418094912644368840442620417414685225340199872975797295511475162170060618806831021437109054760851445152320452665575790602072479287289305203

respectively. This looks like a Hex to decimal conversion. I tried to have the same result in JS but parseInt() and parseFloat() produce something completely different. On top of that JavaScript seems not to like chars in input string and sometimes returns NaN.

Could anyone please provide a function / guidance how to get the same functionality as in Python script?

Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
darth0s
  • 165
  • 2
  • 13
  • I think this is close but it returns floating point... Of course after posting the question I've found almost ready solution here:(http://www.danvk.org/hex2dec.html) – darth0s Sep 27 '17 at 12:31
  • What is purpose of trying to convert string to number? Why do you not utilize string representation of number? – guest271314 Oct 06 '17 at 22:32
  • 1
    @guest271314 i think the OP was not fully aware of js number precision... – Jonas Wilms Oct 08 '17 at 10:11
  • @Jonasw yes, you are right. My main problem here was understanding the precision and overflow. – darth0s Oct 09 '17 at 08:20

3 Answers3

7

Numbers in JavaScript are floating point so they always lose precision after a certain digit. To have unlimited numbers one could rather use an array of numbers from 0 to 9, which has an unlimited range. To do so based on the hex string input, i do a hex to int array conversion, then I use the double dabble algorithm to convert the array to BCD. That can be printed easily:

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

Omar Himada
  • 2,540
  • 1
  • 14
  • 31
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • albeit TheChetan's answer is very infomative as well, this one follows similar idea to the one that actually helped me here: http://www.danvk.org/hex2dec.html – darth0s Oct 09 '17 at 08:24
  • @darth0s yeah i will reward that with my bounty. And while the basic idea to use an array of digits is similar, the algorithm itself is completely different and is hopefully faster than the algorithm youve found – Jonas Wilms Oct 09 '17 at 09:08
3

Using the built in api parseInt, you can get upto 100 digts of accuracy on Firefox and 20 digits of accuracy on 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"

From the ECMAScript Spec,

  1. Let p be ? ToInteger(precision).
    ...
  2. If p < 1 or p > 100, throw a RangeError exception.
TheChetan
  • 4,440
  • 3
  • 32
  • 41
  • 1
    I believe this should be accepted as an answer because it's referring to a a few simple truths: 1) JS numbers are floating point, no matter what; 2) there are browser imposed range and precision limits. This leads to a conclusion that for true big numbers, you'll need a special data types to hold them, whereas `number` will simply not possibly work in general case. – Igor Soloydenko Oct 07 '17 at 23:37
  • @igor but this misses a step: while i think representing the int as either an array or a string is the only possible way, 100 digits are may not accurate enough and the math notation is not requested here so theres a step missing – Jonas Wilms Oct 08 '17 at 10:08
  • This answer is misleading, JavaScript, by design (by how numbers are represented) only allows about 15 digit precision. The Firefox example is only precise to the first 15 digits: `9.02188782898346...`, after this the correct digit sequence is `...223705...`, not `...155080...` as given by both Firefox and Chrome. Do not rely on numbers to accurately represent higher precision than they are designed to do (by the IEEE 754 standard). – Tomas Langkaas Oct 11 '17 at 12:56
0

As described in this answer, JavaScript numbers cannot represent integers larger than 9.007199254740991e+15 without loss of precision.

Working with larger integers in JavaScript requires a BigInt library or other special-purpose code, and large integers will then usually be represented as strings or arrays.

Re-using code from this answer helps to convert the hexadecimal number representation

8079d7ae567dd2c02dadd1068843136314fa3893fa1fb1ab331682c6a85cad62b208d66c9974bbbb15d52676fd9907efb158c284e96f5c7a4914fd927b7326c40efa14922c68402d05ff53b0e4ccda90bbee5e6c473613e836e2c79da1072e366d0d50933327e77651b6984ddbac1fdecf1fd8fa17e0f0646af662a8065bd873

to its decimal representation

90218878289834622370514047239437874345637539049004160177768047103383444023879266805615186962965710608753937825108429415800005684101842952518531920633990402573136677611127418094912644368840442620417414685225340199872975797295511475162170060618806831021437109054760851445152320452665575790602072479287289305203

as demonstrated in the following snippet:

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
);
Tomas Langkaas
  • 4,551
  • 2
  • 19
  • 34