Search code examples
javascriptieee-754

How can I bitwise-cast a 32-bit float into an integer without using typed arrays?


My Arithmetic Expression Compiler, if run in a modern browser, can target both FlatAssembler and GNU Assembler. GNU Assembler doesn't support specifying float values in decimal notation, so my compiler has to convert them into the bit representation using this function:

getIEEE754 = function (decimalNumber) {
  var floatArray = new Float32Array([decimalNumber]);
  var buffer = floatArray.buffer;
  var intArray = new Int32Array(buffer);
  return (
    (highlight ? '<span style="color:#007700">' : "") +
    "0x" +
    intArray[0].toString(16) +
    (highlight ? "<\/span>" : "")
  );
};

However, as Internet Explorer 6 doesn't support Float32Array and Int32Array, that function doesn't work in it. As such, when run in Internet Explorer 6, my web-app can only target Flat Assembler, which supports parsing decimal fractions into IEEE754 floats.

So, how can I make my web-app able to target GNU Assembler if it is being run in Internet Explorer 6?


Solution

  • You can always construct the bit representation manually, using the logarithm to estimate the exponent, then multiplying the number by an appropriate power of two to extract the mantissa bits. Something like this:

    function f32_to_u32(x) {
      if (x !== x)                 /* NaN */
        return 0x7fc00000;
      var signBit = x + (1 / x) < 0 ? 0x80000000 : 0;
    
      x = Math.abs(x);
    
      // use the logarithm to estimate the exponent part,
      // then refine the estimate to correct rounding errors
      var exp = Math.floor(Math.log(x) * Math.LOG2E);
      var mantd = x * Math.pow(2, 53 - exp);
      if (mantd !== Math.floor(mantd))
        exp++;
      if (mantd < Math.pow(2, 53))
        exp--;
    
      // round to 32-bit precision and extract the mantissa
      var mant;
      if (exp >= 128) {            /* infinities */
        return (signBit | 0x7f800000) >>> 0;
      } else if (exp <= -127) {    /* denormals */
        exp = -127;
        mant = Math.round(x * Math.pow(2, 149)) & 0x7fffff;
      } else {
        mant = Math.round(x * Math.pow(2, 23 - exp)) & 0x7fffff;
      }
      exp = (exp + 127) << 23;
    
      return (signBit | exp | mant) >>> 0;
    }
    

    I can safely say this works in IE6, because I tested it in IE5.