[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Public WebGL] The miserable state of affairs of floating point support



Here would be a somewhat comical and horrendously inefficient implementation of Float16Array. While that works for normal JS code, somewhat at least, it'd still run into trouble with texImage2D/bufferData/TypedArray functionalities etc. Is this really where we ant to be?

var twoPm14 = Math.pow(2, -14);
var smallest = twoPm14/1024;
var largest = Math.pow(2, 30-15) * (1 + 1023/1024);

var toHalf = (function() {
  var floatView = new Float32Array(1);
  var int32View = new Int32Array(floatView.buffer);

  return function toHalf( fval ) {
    floatView[0] = fval;
    var fbits = int32View[0];
    var sign  = (fbits >> 16) & 0x8000;          // sign only
    var val   = ( fbits & 0x7fffffff ) + 0x1000; // rounded value

    if( val >= 0x47800000 ) {             // might be or become NaN/Inf
      if( ( fbits & 0x7fffffff ) >= 0x47800000 ) {
                                          // is or must become NaN/Inf
        if( val < 0x7f800000 ) {          // was value but too large
          return sign | 0x7c00;           // make it +/-Inf
        }
        return sign | 0x7c00 |            // remains +/-Inf or NaN
            ( fbits & 0x007fffff ) >> 13; // keep NaN (and Inf) bits
      }
      return sign | 0x7bff;               // unrounded not quite Inf
    }
    if( val >= 0x38800000 ) {             // remains normalized value
      return sign | val - 0x38000000 >> 13; // exp - 127 + 15
    }
    if( val < 0x33000000 )  {             // too small for subnormal
      return sign;                        // becomes +/-0
    }
    val = ( fbits & 0x7fffffff ) >> 23;   // tmp exp for subnormal calc
    return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit
         + ( 0x800000 >>> val - 102 )     // round depending on cut off
         >> 126 - val );                  // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
  };
}());

var fromHalf = function(n){
    var sign = 1 - ((n & 0x8000) >> 14);
    var exponent = (n & 0x7c00) >> 10;
    var mantissa = (n & 0x03ff);

    if(exponent === 0){
        if(mantissa !== 0){
            return sign * twoPm14 * (mantissa/1024);
        }
        else{
            return sign * 0;
        }
    }
    else if(exponent < 31){
        return sign * Math.pow(2, exponent-15) * (1 + mantissa/1024);
    }
    else{
        if(mantissa === 0){
            return sign * Infinity;
        }
        else{
            return NaN;
        }
    }
};

var Float16Array = function(size){
    var array = this.array = new Uint16Array(size);
    var self = this;

    var defineProperty = function(n){
        Object.defineProperty(self, n, {
            get: function(){
                return fromHalf(array[n]);
            },
            set: function(value){
                return array[n] = toHalf(value);
            }
        });
    }

    for(var i=0; i<size; i++){
        defineProperty(i.toFixed(0));
    }
}

var foo = new Float16Array(1000);