JavaScript and IEEE754

In C/C++ and... well, practically any other language intended for systems programming, you can get at the bits of a floating point number, either by tricks with unions or binary data packing routines. And all of those languages use the IEEE754 standard for floating point number representation in memory.

JavaScript uses IEEE754 internally as well, but offers neither unions nor general binary data manipulation. So if you want convert to and from bytes - say, dug out of a base64-encoded blob - you need to do some work.

Here's a first crack at conversion functions. They are not thoroughly tested. Caveats: "negative 0" is not preserved, denormalized numbers aren't handled, nor are QNaNs.

Don't use this - see the code in Typed Array Polyfill instead
// Convert a JavaScript number to IEEE-754 Double Precision
// value represented as an array of 8 bytes (octets)
//
function toIEEE754(v) {
    var s, e, f;
    if (isNaN(v)) {
        e = 2047; f = 1; s = 0;
    } 
    else if (v === Infinity) {
        e = 2047; f = 0; s = 0;
    } 
    else if (v === -Infinity) {
        e = 2047; f = 0; s = 1;
    }
    else if (v === 0 && 1/v === -Infinity) { // -0
        e = 0; f = 0; s = 1;
    }
    else if (v === 0) {
        e = 0; f = 0; s = 0;
    }
    else {
        if (v < 0) {
            s = 1;
            v = Math.abs(v);
        }
        else {
            s = 0;
        }

        var ln = Math.floor( Math.log(v) / Math.log(2) );

        if (ln > -1022) {
            e = ln + 1023;
            f = (v / Math.pow(2, ln) - 1) * 0x10000000000000;
        }
        else {
            e = 0;
            f = (v / Math.pow(2,-1022)) * 0x10000000000000;
        }
    }

    return [
        ((s & 0x1) << 7) | ((e >> 4) & 0x7f),
        ((e & 0x0f) << 4) | ((f / Math.pow(2,48)) & 0x0f),
        (f / Math.pow(2,40)) & 0xff,
        (f / Math.pow(2,32)) & 0xff,
        (f / Math.pow(2,24)) & 0xff,
        (f / Math.pow(2,16)) & 0xff,
        (f / Math.pow(2,08)) & 0xff,
        f & 0xff        
    ];
}

// Convert an IEEE-754 Double Precision value represented
// as an array of 8 bytes (octets) to a JavaScript number
//
function fromIEEE754(bytes) {

    var s = (bytes[0] >> 7) & 0x1;
    var e = ((bytes[0] & 0x7f) << 4) | ((bytes[1] & 0xf0) >> 4);

    var f = ((bytes[1] & 0x0f) * Math.pow(2,48)) +
            (bytes[2] * Math.pow(2,40)) +
            (bytes[3] * Math.pow(2,32)) +
            (bytes[4] * Math.pow(2,24)) +
            (bytes[5] * Math.pow(2,16)) +
            (bytes[6] * Math.pow(2, 8)) +
            bytes[7];

    if (e === 2047) {
        if (f !== 0) {
            return Number.NaN;
        }
        else if (s) {
            return -Infinity;
        }
        else {
            return Infinity;
        }
    }
    else if (e > 0) {
        return (s?-1:1) * Math.pow(2,e-1023) * (1 + f / 0x10000000000000);
    }
    else if (f !== 0) {
        return (s?-1:1) * Math.pow(2,-1022) * (f / 0x10000000000000);
    }
    else {
        return s ? -0 : 0;
    }
}


EDITED: Corrected parsing of +/-Infinity, and serializing of -0
EDITED: Corrected serializing of denormalized values

Comments