1

I have JavaScript code that retrieves numerical vectors from a web-service. The original data is an array of doubles that is converted to a byte array and then base64 encoded. I decode from base64 in JavaScript, but then I don't know how to transform the resulting bytes into an array of numbers.

Abiel
  • 4,595
  • 6
  • 47
  • 66
  • I'm not sure what your data looks like, but you can make an array from delimited data using .split("delimiterCharacter") – Diodeus - James MacFarlane Dec 02 '11 at 18:49
  • Uh, double data as byte sequence - I think, you'll have to implement this manually. [In this question](http://stackoverflow.com/questions/2905556/how-can-i-convert-a-byte-array-into-a-double-and-back) I found an algorithm to convert a double into bytes, maybe you can reverse it. – Yogu Dec 02 '11 at 18:51
  • 2
    Can you provide some sample data? – Alec Gorge Dec 02 '11 at 18:51
  • @alecgorge Here is what would be printed with console.log() after base64 decoding: l²@ù±@*±@±@Ó°@¾¯@â®@&®@ ­@­@©@ ©@¶¨@Æ©@À§@V©@ä§@²¨@¬§@ʧ@Ч@§@²©@Ú¨@¥@©@¶§@À¥@¦@L¦@è¦@¦@ª¦@¦@§@§@(§@N¨@~¨@¨@©@ª@¨@l«@Vª@îª@¦«@¨@D«@v«@4«@à«@ª­@«@­@r­@þ¬@,®@b®@Ư@¯@Ø®@Ì®@D°@n°@,°@Ø®@æ¬@ȯ@1°@|°@h°@h°@v°@z¯@¾°@r°@̯@·°@Я@ü¯@â¯@¯@`¯@z®@ö®@¬@j¬@°©@>«@d©@¨@¨@H¥@À¤@î¤@`¤@¡@Æ¢@Ê @Ø@* @T @@@@Ü @Ä@@0@@Þ @¡@¸¢@î¢@¢@¨£@ø£@ú¢@¤@¤@£@ä¢@¥@Ò¥@¤@J¥@&¦@¨¦@ – Abiel Dec 02 '11 at 19:10
  • @Abiel That is a bit unwieldy. How about the base64 encoded data in a [gist](http://gist.github.com/)? – Alec Gorge Dec 02 '11 at 19:11
  • @alecgorge https://gist.github.com/1424455 – Abiel Dec 02 '11 at 19:14
  • Is the first double in the example array the value of `4716.0`? – J. Holmes Dec 02 '11 at 19:44
  • @32bitkid That's correct – Abiel Dec 02 '11 at 19:48

2 Answers2

3

This was the only way I could think of off the top of my head to do it.

function bytesToDouble(str,start) {
    start *= 8;
    var data = [str.charCodeAt(start+7),
                str.charCodeAt(start+6),
                str.charCodeAt(start+5),
                str.charCodeAt(start+4),
                str.charCodeAt(start+3),
                str.charCodeAt(start+2),
                str.charCodeAt(start+1),
                str.charCodeAt(start+0)];

    var sign = (data[0] & 1<<7)>>7;

    var exponent = (((data[0] & 127) << 4) | (data[1]&(15<<4))>>4);

    if(exponent == 0) return 0;
    if(exponent == 0x7ff) return (sign) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;

    var mul = Math.pow(2,exponent - 1023 - 52);
    var mantissa = data[7]+
        data[6]*Math.pow(2,8*1)+
        data[5]*Math.pow(2,8*2)+
        data[4]*Math.pow(2,8*3)+
        data[3]*Math.pow(2,8*4)+
        data[2]*Math.pow(2,8*5)+
        (data[1]&15)*Math.pow(2,8*6)+
        Math.pow(2,52);

    return Math.pow(-1,sign)*mantissa*mul;
}

var data = atob("AAAAAABsskAAAAAAAPmxQAAAAAAAKrF");

alert(bytesToDouble(data,0)); // 4716.0
alert(bytesToDouble(data,1)); // 4601.0

This should give you a push in the right direction, though it took me a while to remember how to deal with doubles.

One big caveats to note though:

This relies on the atob to do the base64 decoding, which is not supported everywhere, and aside from that probably isn't a great idea anyway. What you really want to do is unroll the base64 encoded string to an array of numbers (bytes would be the easiest to work with although not the most efficient thing on the planet). The reason is that when atob does its magic, it returns a string, which is far from ideal. Depending on the encoding the code points it maps to (especially for code points between 128 and 255) the resulting .charCodeAt() may not return what you expect.

And there may be some accuracy issues, because after all I am using a double to calculate a double, but I think it might be okay.

Base64 is fairly trivial to work with, so you should be able to figure that part out.

If you did switch to an array (rather than the str string now), then you would obviously drop the .charCodeAt() reference and just get the indices you want directly.

There is a functioning fiddle here

J. Holmes
  • 17,946
  • 5
  • 43
  • 51
  • Thanks. This worked for me. I was using some other code to decode base64, so not too concerned about `atob` issue. I'll keep an eye out for accuracy issues, but in general my data doesn't need exceptionally high precision, so it should be OK. – Abiel Dec 02 '11 at 21:38
0

I assume we have used this function in web service (c#) to encode the double array data as string:

//Input: [552.4, 539.8]
//Output: IOz0gCAsscA=

private string ConvertToSerializableString(double[] input)
{
  byte[] data = new byte[input.Length * 4];
  for (int i = 0; i < input.Length; i++)
  {
    int source = (int)(input[i] * 1E6);    
    int dataIndex = i * 4;
    data[dataIndex] = (byte)((source >> 24) & 0xFF);
    data[dataIndex + 1] = (byte)((source >> 16) & 0xFF);
    data[dataIndex + 2] = (byte)((source >> 8) & 0xFF);
    data[dataIndex + 3] = (byte)(source & 0xFF);
  }

  return Convert.ToBase64String(data);          
}

Then we can use the following client script (javascript) to decode it:

var base64EncodedDoubleArrayData = "IOz0gCAsscA=";
var byteData = window.atob(base64EncodedDoubleArrayData);                

var doubleArray = [];
for (var iColumn = 0; iColumn < byteData.length; iColumn = iColumn + 4) 
{
   var item = (byteData.charCodeAt(iColumn) << 24) + (byteData.charCodeAt(iColumn + 1) << 16) + (byteData.charCodeAt(iColumn + 2) << 8) + byteData.charCodeAt(iColumn + 3);   
   var doubleResult = parseFloat(item/1e6);
   doubleArray.push(doubleResult);
}  

//it should return something like doubleArray = [552.4, 539.8]
Minh Nguyen
  • 1,740
  • 1
  • 21
  • 30