Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2010-11-28

IEEE754その2

12:18

なんか思いついてみた。

<!DOCTYPE html>
<meta charset="utf-8">
<title>IEEE754</title>
<pre id="log"></pre>
<script>

function print(msg) {
  document.getElementById('log').innerHTML += msg + '\n';
}

window.onload = function() {
  // prepare
  var _bit2num = {};
  for (var i = 0; i < 0x100; ++i) {
    _bit2num[("0000000" + i.toString(2)).slice(-8)] = i;
  }
  var _IEEE754positive = /^.(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})$/;
  var _IEEE754negative =  /^(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})$/;

  var input = [];
  for (var i = 1; i < 100000; i += 2) {
    input[i - 1] = i / Math.PI;
    input[i] = - i / Math.PI;
  }

  function test1(input) {
    var rv = [], mix, hash, sign, exp, frac, ary, _pooledArray;

    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

      hash = _bit2num;
      sign = mix < 0;
      sign && (mix *= -1);

      // add offset 1023 to ensure positive
      // 0.6931471805599453 = Math.LN2;
      exp  = ((Math.log(mix) / 0.6931471805599453) + 1023) | 0;

      // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
      // then throw away trash less than decimal point
      frac = (Math.floor(mix * Math.pow(2, 52 + 1023 - exp))).
              toString(2).slice(1);

      // exp is between 1 and 2047. make it 11 bits
      // http://d.hatena.ne.jp/uupaa/20101128
      _pooledArray = !sign ? _IEEE754positive.exec((exp + 4096).toString(2) + frac)
                           : _IEEE754negative.exec((exp + 2048).toString(2) + frac);
      ary = _pooledArray; // alias
      rv.push(0xcb, hash[ary[1]], hash[ary[2]],
                    hash[ary[3]], hash[ary[4]],
                    hash[ary[5]], hash[ary[6]],
                    hash[ary[7]], hash[ary[8]]);
    }

    return rv;
  }


  function test2(input) {
    var rv = [], mix, sign, exp, frac, upper, r1, r2, r3, r4, r5 ,r6, r7, r8;
    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

      sign = mix < 0;
      sign && (mix *= -1);

      // add offset 1023 to ensure positive
      // 0.6931471805599453 = Math.LN2;
      exp  = ((Math.log(mix) / 0.6931471805599453) + 1023) | 0;

      // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
      // then throw away trash less than decimal point
      frac = (Math.floor(mix * Math.pow(2, 52 + 1023 - exp))).toString(2);

      // 13 bits = "1" (no meaning) + 1 bit (sign) + 11 bits (exp)
      // 6144 = 2048 + 4096 = 1100000000000 (2)
      upper = (exp + (sign ? 6144 : 4096)).toString(2);

      // charCode for '0' => 48, '1' => 49

      r8 = ((frac.charCodeAt(52) - 48)     ) + ((frac.charCodeAt(51) - 48) << 1) +
           ((frac.charCodeAt(50) - 48) << 2) + ((frac.charCodeAt(49) - 48) << 3) +
           ((frac.charCodeAt(48) - 48) << 4) + ((frac.charCodeAt(47) - 48) << 5) +
           ((frac.charCodeAt(46) - 48) << 6) + ((frac.charCodeAt(45) - 48) << 7) ;

      r7 = ((frac.charCodeAt(44) - 48)     ) + ((frac.charCodeAt(43) - 48) << 1) +
           ((frac.charCodeAt(42) - 48) << 2) + ((frac.charCodeAt(41) - 48) << 3) +
           ((frac.charCodeAt(40) - 48) << 4) + ((frac.charCodeAt(39) - 48) << 5) +
           ((frac.charCodeAt(38) - 48) << 6) + ((frac.charCodeAt(37) - 48) << 7) ;

      r6 = ((frac.charCodeAt(36) - 48)     ) + ((frac.charCodeAt(35) - 48) << 1) +
           ((frac.charCodeAt(34) - 48) << 2) + ((frac.charCodeAt(33) - 48) << 3) +
           ((frac.charCodeAt(32) - 48) << 4) + ((frac.charCodeAt(31) - 48) << 5) +
           ((frac.charCodeAt(30) - 48) << 6) + ((frac.charCodeAt(29) - 48) << 7) ;

      r5 = ((frac.charCodeAt(28) - 48)     ) + ((frac.charCodeAt(27) - 48) << 1) +
           ((frac.charCodeAt(26) - 48) << 2) + ((frac.charCodeAt(25) - 48) << 3) +
           ((frac.charCodeAt(24) - 48) << 4) + ((frac.charCodeAt(23) - 48) << 5) +
           ((frac.charCodeAt(22) - 48) << 6) + ((frac.charCodeAt(21) - 48) << 7) ;

      r4 = ((frac.charCodeAt(20) - 48)     ) + ((frac.charCodeAt(19) - 48) << 1) +
           ((frac.charCodeAt(18) - 48) << 2) + ((frac.charCodeAt(17) - 48) << 3) +
           ((frac.charCodeAt(16) - 48) << 4) + ((frac.charCodeAt(15) - 48) << 5) +
           ((frac.charCodeAt(14) - 48) << 6) + ((frac.charCodeAt(13) - 48) << 7) ;

      r3 = ((frac.charCodeAt(12) - 48)     ) + ((frac.charCodeAt(11) - 48) << 1) +
           ((frac.charCodeAt(10) - 48) << 2) + ((frac.charCodeAt( 9) - 48) << 3) +
           ((frac.charCodeAt( 8) - 48) << 4) + ((frac.charCodeAt( 7) - 48) << 5) +
           ((frac.charCodeAt( 6) - 48) << 6) + ((frac.charCodeAt( 5) - 48) << 7) ;

      r2 = ((frac.charCodeAt( 4) - 48)     ) + ((frac.charCodeAt( 3) - 48) << 1) +
           ((frac.charCodeAt( 2) - 48) << 2) + ((frac.charCodeAt( 1) - 48) << 3) +
           ((upper.charCodeAt(12) - 48) << 4) + ((upper.charCodeAt(11) - 48) << 5) +
           ((upper.charCodeAt(10) - 48) << 6) + ((upper.charCodeAt( 9) - 48) << 7) ;

      r1 = ((upper.charCodeAt( 8) - 48)     ) + ((upper.charCodeAt( 7) - 48) << 1) +
           ((upper.charCodeAt( 6) - 48) << 2) + ((upper.charCodeAt( 5) - 48) << 3) +
           ((upper.charCodeAt( 4) - 48) << 4) + ((upper.charCodeAt( 3) - 48) << 5) +
           ((upper.charCodeAt( 2) - 48) << 6) + ((upper.charCodeAt( 1) - 48) << 7) ;

      rv.push(0xcb, r1, r2, r3, r4, r5, r6, r7, r8);
    }

    return rv;
  }


  var t = new Date;
  var rv = test1(input);
  print('test1 took ' + (new Date - t) + ' ms, results : ' + rv.slice(0, 10) + ' ...');

  var t = new Date;
  var rv = test2(input);
  print('test2 took ' + (new Date - t) + ' ms, results : ' + rv.slice(0, 10) + ' ...');
  
};
  
</script>

結果 (ms)。

test1 test2
Opera 742 516
Chrome 784 438
IE9pp7 851 431
IE8 (10分の1ループ) 378 389
Firefox 3.6 1185 1659

うーん、悩ましい。イェーガーモンキーはどうなんだろ。


関連。


また思いついた

  function test3(input) {
    var rv = [], mix, sign, exp, frac, r1, r2, r3, r4, r5 ,r6, r7, r8;
    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

      sign = mix < 0;
      sign && (mix *= -1);

      // exp => 11 bits
      // add offset 1023 to ensure positive
      // 0.6931471805599453 = Math.LN2;
      exp  = ((Math.log(mix) / 0.6931471805599453) + 1023) | 0;

      // frac => 53 bits
      // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
      // then throw away trash less than decimal point
      frac = Math.floor(mix * Math.pow(2, 52 + 1023 - exp));

      r8 = frac % 256;
      frac = (frac - r8) / 256; // frac is 45 bits

      r7 = frac % 256;
      frac = (frac - r7) / 256; // frac is 37 bits

      r6 = frac % 256;
      frac = (frac - r6) / 256; // frac is 29 bits

      r5 = frac & 255;
      frac = frac >> 8; // frac is 21 bits

      r4 = frac & 255;
      frac = frac >> 8; // frac is 13 bits

      r3 = frac & 255;
      frac = frac >> 8; // frac is 5 bits

      // take the rest 4 bits out of 5 from frac, and 4 bits from the exp
      r2 = (frac & 15) + ((exp & 15) << 4);

      // take the rest 7 bits of exp, then add sign bit
      r1 = exp >> 4;
      sign && (r1 += 128);

      rv.push(0xcb, r1, r2, r3, r4, r5, r6, r7, r8);
    }

    return rv;
  }

再度計測。上との差は誤差ということで。

test1 test2 test 3
Opera 724 531 56
Chrome 795 426 84
IE9pp7 551 333 130
IE8 (10分の1ループ) 287 388 50
Firefox 3.6 1163 1638 222

速い!

トラックバック - http://javascript.g.hatena.ne.jp/edvakf/20101128
 |