RenegadeMaster88
RenegadeMaster88

Reputation: 41

Converting floats to strings in javascript

Ok i want to save an array of THREE.Vector3 efficiently to local storage. Since javascript works using Strings, i want to convert a 32bit float to a string using the most efficient bit ratio. i.e ideally 32 bit float = 4 * 8 Bit which is easy to do in something like C++

the problem seems to be 1st Javascript strings are UTF which includes some padding

http://en.wikipedia.org/wiki/UTF-8

and secondly the code i am currently using 0 get converted '' and then omitted, making the converted byte length un-reliable.

String.fromCharCode(0) == ''

var float2str = function(num)
{
    var bytestream = new Array();
    var view = new DataView(new ArrayBuffer(4));
    view.setFloat32(0,num);

    bytestream.push(view.getUint8(0));
    bytestream.push(view.getUint8(1));
    bytestream.push(view.getUint8(2));
    bytestream.push(view.getUint8(3));
    return String.fromCharCode(view.getUint8(0),view.getUint8(1),view.getUint8(2),view.getUint8(3))

}

var str2float = function(str)
{

    var bytestream = unpack(str)
    var view = new DataView(new ArrayBuffer(4));
    view.setUint8(0,bytestream[0]);
    view.setUint8(1,bytestream[1]);
    view.setUint8(2,bytestream[2]);
    view.setUint8(3,bytestream[3]);
    return view.getFloat32(0);
}

thanx!

Upvotes: 1

Views: 1564

Answers (2)

Tapio
Tapio

Reputation: 3456

Use base64 encoding. It will convert 6 bits at a time to a printable ASCII character (which will be the same in UTF-8). As such, you will actually need 5.33 bytes for one float (the fraction is usable, i.e. it's not 6 bytes as long as you encode the array as a whole and not as individual floats), but that should still give some space savings.

Here's a function (not mine) to convert directly from ArrayBuffer to base64 string: https://gist.github.com/958841

Upvotes: 0

JasonWyatt
JasonWyatt

Reputation: 5303

The trick is in getting the string value of the unsigned 8-bit integers to be printable. I found that if your 8-bit numbers are too small or too large (outside of the "bread and butter" range of ASCII values) you will end up with something that is unprintable. So instead of creating a string of length four using the four ASCII values of the bytes from the float, we can use a string of length 8 using 4-bit values from the float and offsetting those values into the printable range (+32). Here's my solution:

var offset = 33;

var float2str = function(num){
    var view = new DataView(new ArrayBuffer(4));
    view.setFloat32(0,num);

    var fourbits = [];
    for ( var i = 0; i < 4; i++ ){
        // split each byte into two 4-bit values
        fourbits.push(view.getUint8(0) >> 4);
        fourbits.push(view.getUint8(0) & 15);
    }

    for ( var i = 0; i < 8; i++ ){
        // build a string with offset four-bit values
        fourbits[i] = String.fromCharCode(offset+fourbits[i]);
    }
    return fourbits.join('');

};

var str2float = function(str){
    var bytestream = str;
    var view = new DataView(new ArrayBuffer(4));

    for(var i = 0; i < 4; i++){
        // re-convert the characters into bytes.
        view.setUint8(i, ((bytestream[i*2].charCodeAt() - offset) << 4) + bytestream[i*2+1].charCodeAt() - offset);
    }

    return view.getFloat32(0);
};

console.log(float2str('2.251')); // "%!\"!\"!'#"
console.log(str2float(float2str('2.251'))); // 2.250999927520752

I hope this helps.

Upvotes: 1

Related Questions