asehgal asehgal - 4 months ago 9
Javascript Question

From double to float in JavaScript?

My understanding is that all numbers in JavaScript are double-precision floating-point. How can I convert a double to a float in JavaScript? I understand the precision loss, but that is tolerable.

I need to depict C style floating point numbers

Answer

This will work in modern engines:

function doubleToFloat ( d ) {
    if ( Float32Array )
        return new Float32Array([d])[0];
}

Note that the data type of the result is still number (double precision), but it only uses the bits that a single-precision float would also have.

EDIT:

I added an incomplete! fallback for the case that Float32Array is not available:

function doubleToFloat ( d ) {
    if ( Float32Array )
        return new Float32Array([d])[0];

    var sign = 2*(d >= 0) - 1;
    var b = Math.abs( d ).toString( 2 );
    var decimalIndex = b.indexOf( '.' );
    var oneIndex = b.indexOf( '1' );
    var exponent, mantissa, result;

    if( decimalIndex === -1 ) {
        exponent = b.length - 1;
        mantissa = b.substr( 0, 23 );
        result = sign*parseInt( mantissa, 2 )*Math.pow( 2, 1 + exponent - mantissa.length );
    } else if ( decimalIndex === 1 ) {
        exponent = 1 - oneIndex;       
        if ( oneIndex === 0 ) {
            mantissa = '1' + b.substr( 2, 23 );
            result = sign*parseInt( mantissa, 2 )*Math.pow( 2, 1 - mantissa.length );
        } else {
            mantissa = b.substr( oneIndex, 23 );
            result = sign*parseInt( mantissa, 2 )*Math.pow( 2, 1 + exponent - mantissa.length );
        }
    } else {
        exponent = decimalIndex - 1;
        mantissa = b.replace( '.', '' ).substr( 0, 23 );
        result = sign*parseInt( mantissa, 2 )*Math.pow( 2, decimalIndex - mantissa.length );
    }

    if ( exponent < -127 || exponent > 128 )
        throw new RangeError( 'Double ' + d + ' outside single-precision range' );

    return result;
}

It seems to work pretty well, but it is not perfect. I have not tested it on many inputs, and I have made no attempt at rounding correctly.

Tests:

const RealFloat32Array = Float32Array;
Float32Array = false;

const tests = [
    -5, 0, 1, 1.01, 0.125, 0.00009,
    1.0000000001,
    10000.12312421321,
    500000000.001,
    5000000000.001,
    50000000000.001,
    3.402823e+38
];

function test ( d ) {

    const usingFloat32 = new RealFloat32Array([d])[0]
    const usingAlgorithm = doubleToFloat( d );

    if ( usingFloat32 !== usingAlgorithm )
        console.error( 'Test FAILED: ', { d, usingFloat32, usingAlgorithm } );

}

tests.forEach( test );

Result (9 passes, 3 fails):

Test FAILED:  Object {d: 0.00009, usingFloat32: 0.00009000000136438757, usingAlgorithm: 0.00008999998681247234}
Test FAILED:  Object {d: 50000000000.001, usingFloat32: 49999998976, usingAlgorithm: 49999994880}
Test FAILED:  Object {d: 3.402823e+38, usingFloat32: 3.4028230607370965e+38, usingAlgorithm: 3.4028228579130005e+38}

Those are probably all caused by the fact that I don't do any rounding.

Comments