3

I recently found out I can convert a Float32 into an array of bytes that represent it - as such:

let number = Math.PI;
let bytes = new Uint8Array(new Float32Array([number]).buffer); // [219, 15, 73, 64]

Is there a way to convert bytes back into the Float32, in a clean way?

DavidsKanal
  • 655
  • 11
  • 15
  • Does this answer your question? [Read/Write bytes of float in JS](https://stackoverflow.com/questions/4414077/read-write-bytes-of-float-in-js) – user202729 Jan 30 '21 at 10:48

1 Answers1

3

Is there a way to convert bytes back into the Float32

You don't need to convert it, it's already there! you just need to read it from the float32 view. However in your example you didn't save a reference to the float32 view...

Typed arrays work very differently to other numbers in JavaScript. The key is to think about the buffer and views independently - that is, Float32Array and Uint8Array are merely views into a buffer (a buffer is just a fixed sized contiguous block of memory, which is why typed arrays are so fast).

In your example when you called new Float32Array you passed it an array with a single number to initialise it, but you didn't pass it a buffer, this causes it to create a buffer for you of the appropriate length (4 bytes). When you called new Uint8Array you passed it a buffer instead, this doesn't cause it to merely copy the buffer, but it actually uses it directly. The below example is equivalent to yours, but retains all references and makes the above assertions more obvious:

const number = Math.PI

const buffer = new ArrayBuffer(4);
const f32 = new Float32Array(buffer); // [0]
const ui8 = new Uint8Array(buffer); // [0, 0, 0, 0]

f32[0] = number;
f32 // [3.1415927410125732]
ui8 // [219, 15, 73, 64]

ui8[3] = 1;
f32 // [3.6929245196445856e-38]
ui8 // [219, 15, 73, 1]

As you can see there is no need to "convert" above, as both views share the same buffer, any change via one view is instantly available in the other.

This is actually a good way to play with and understand floating point formats. Also use ui8[i].toString(2) to get the raw binary and use ui8[i] = parseInt('01010101', 2) set raw binary for each byte where i is 0-3. Note that you cannot set the raw binary through the f32 view as it will interpret your number numerically and break it into the significand and exponent, however you may want to do this to see how the numerical binary is converted into the float32 format.

Thomas Brierley
  • 1,187
  • 1
  • 7
  • 8
  • Holy crap, this is too cool, thanks. It really does help to look at Float32Arrays as simply views onto a buffer. Regarding IEEE 754, I actually used that to implement my own "Float to bytes" algorithm about a year ago, but now that I found out this could be done natively (and probably faster), I guess I'll use this instead. :) – DavidsKanal Aug 31 '18 at 13:16
  • 1
    Glad it helped, it's really good that you implemented your own as a learning exercise :) you'd be surprised how uncommon a decent understanding of the floating point format is, it's also endlessly deceptively complex when you start exploring it's limitations and pitfalls! One can never really claim to know all of the implications of floating point math. – Thomas Brierley Aug 31 '18 at 13:22
  • Haha, indeed! But yeah, I suggest everyone to at least check out the format under the hood because it's absolutely essential to any programming language, basically. Also, one thing regarding your post, I think the Float32 representation of Math.PI is 3.1415927410125732, not 3.141592653589793 - since some of the precision is lost. – DavidsKanal Aug 31 '18 at 13:47
  • 1
    Aha yes, you spotted my lazy copy pasting. Fixed. It's also a bit confusing how the f32 decimal significand appears to be longer in spite of the binary significand being shorter (because of how it's cast to a f64 before being printed as a decimal). – Thomas Brierley Aug 31 '18 at 13:54
  • Yeah, those things are weird - handle with caution. ;P – DavidsKanal Aug 31 '18 at 14:06