Search code examples
javascripttypedarray

About the binary format of JavaScript TypedArray float32


I need to write a piece of hardware emulator in JavaScript. It has its own floating point format, so I do lots of conversion between JS numerics and that format, which is slow. I have the idea to use JavaScript TypedArray float32 since I have direct access of bytes forming the float32 floating point value which is not so far from the desired format, so the conversion would be much faster this way (only some shifts, etc, using Uint8 view of the Float32).

However, I am not sure how portable solution it would be. Various documents about TypedArray topics states that float32 is like "the native C format on that hardware" or such. But can I expect that the exact binary format of float32 is the same on all platforms running some browser/JS? I can guess the endiannes can be a problem, but I can deal with that if at least there are no other differences. As far as I can tell, the format used seems to be IEE754, but there can be others used to implement float32 in JS on some (well, at least not so exotic ...) platforms?

I could test at least x86 and Apple A7 CPUs, and it seems they are the very same, which is good, but also odd, as I thought the byte order of these CPUs are different (maybe not the floating format at least?). However it's far from being a global truth just checking two platforms/OSes/browsers/whatever ...


Solution

  • The Float32Array will internally represent the values bases on the endianess of the host system, typically little-endian.

    And yes, the format is IEEE 754 (this has been around since before FPUs came along, and the variations of it deals with more the width, ie. 64-bit, 80-bit and so on). All numbers (Number) in JavaScript is internally represented as 64-bit IEEE 754. For typed arrays both 32-bit and 64-bit IEEE 754 is of course available.

    PowerPC and 68k CPUs uses big-endian (the way it ought to be! :) ). The so-called network order is also big-endian, and many platform-independent file formats are stored in big-endian byte order (in particular with audio and graphics). Most mainstream computers uses little-endian CPUs such as the x86. So in cases with a combination of these you very likely have to deal with different byte orders.

    To deal with endianess you can instead of using a Float32Array use a DataView.

    For example:

    var buffer = new ArrayBuffer(10240);    // some raw byte buffer
    var view = new DataView(buffer);        // flexible view supporting endianness
    

    Now you can now read and write to any position in the buffer with endianess in mind (DataView also allow reading/writing from/to non-aligned positions, ie. you can write a Float32 value to position 3 if you need to. You cannot do this with Float32/Uint32/Int16 etc.).

    The browser will internally convert to the correct order - you just provide the value as-is:

    view.setFloat32(pos, 0.5);              // big-endian
    view.setFloat32(pos, 0.5, false)        // big-endian
    view.setFloat32(pos, 0.5, true);        // little-endian
    

    And likewise when reading:

    var n = view.getFloat32(pos);           // big-endian
    var n = view.getFloat32(pos, false)     // big-endian
    var n = view.getFloat32(pos, true);     // little-endian
    

    Tip: You can use a native Float32Array internally and later read/write to it using endianess. This tend to be speedier but it requires a conversion using DataView at the end if the resulting buffer's endianess is different from the host system:

    var f32 = new Float32Array(buffer);     // or use a size
    f32[0] = 0.5;
    

    Then to make sure you have big-endian representation:

    var view = new DataView(f32.buffer);
    var msbVal = view.getFloat32(0):        // returns 32-bit repres. in big-endian
    

    Hope that gave some inputs! Just throw me questions about it if you want me to elaborate on some part.