emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js
author Martin Soch <Martin.Soch@oracle.com>
Thu, 07 Feb 2013 17:41:41 +0100
brancharithmetic
changeset 700 b9bf26ea0118
parent 698 ff57af563cb8
child 737 b2731af0357d
permissions -rw-r--r--
Fixed negation for Integer.MIN_VALUE
     1 // empty line needed here
     2 Number.prototype.add32 = function(x) { return (this + x) | 0; };
     3 Number.prototype.sub32 = function(x) { return (this - x) | 0; };
     4 Number.prototype.mul32 = function(x) { 
     5     return (((this * (x >> 16)) << 16) + this * (x & 0xFFFF)) | 0;
     6 };
     7 Number.prototype.neg32 = function() { return (-this) | 0; };
     8 
     9 Number.prototype.toInt8 = function()  { return (this << 24) >> 24; };
    10 Number.prototype.toInt16 = function() { return (this << 16) >> 16; };
    11 
    12 var __m32 = 0xFFFFFFFF;
    13 
    14 Number.prototype.next32 = function(low) {
    15   if (this === 0) {
    16     return low;
    17   }
    18   var l = new Number(low);
    19   l.hi = this | 0;
    20   return l;
    21 };
    22 
    23 Number.prototype.high32 = function() { 
    24     return this.hi ? this.hi : (Math.floor(this / (__m32+1))) | 0;
    25 };
    26 Number.prototype.toInt32 = function() { return this | 0; };
    27 Number.prototype.toFP = function() {
    28     return this.hi ? this.hi * (__m32+1) + this : this;
    29 };
    30 Number.prototype.toLong = function() {
    31     var hi = (this > __m32) ? (Math.floor(this / (__m32+1))) | 0 : 0;
    32     return hi.next32(Math.floor(this % (__m32+1)));
    33 };
    34 
    35 Number.prototype.toExactString = function() {
    36     if (this.hi) {
    37         // check for Long.MIN_VALUE
    38         if ((this.hi == (0x80000000 | 0)) && (this == 0)) {
    39             return '-9223372036854775808';
    40         }
    41         var res = 0;
    42         var a = [ 6,9,2,7,6,9,4,9,2,4 ];
    43         var s = '';
    44         var digit;
    45         var neg = this.hi < 0;
    46         if (neg) {
    47             var x = this.neg64();
    48             var hi = x.hi;
    49             var low = x;
    50         } else {
    51             var hi = this.hi;
    52             var low = this;
    53         }
    54         for (var i = 0; i < a.length; i++) {
    55             res += hi * a[i];
    56             var low_digit = low % 10;
    57             digit = (res % 10) + low_digit;
    58 
    59             low = Math.floor(low / 10);
    60             res = Math.floor(res / 10);
    61 
    62             if (digit >= 10) {
    63                 digit -= 10;
    64                 res++;
    65             }
    66             s = String(digit).concat(s);
    67         }
    68         s = String(res).concat(s).replace(/^0+/, '');
    69         return (neg ? '-' : '').concat(s);
    70     }
    71     return String(this);
    72 };
    73 
    74 Number.prototype.add64 = function(x) {
    75     var low = this + x;
    76     carry = 0;
    77     if (low > __m32) {
    78         carry = 1;
    79         low -= (__m32+1);
    80     }
    81     var hi = (this.high32() + x.high32() + carry) | 0;
    82     return hi.next32(low);
    83 };
    84 
    85 Number.prototype.sub64 = function(x) {
    86     var low = this - x;
    87     carry = 0;
    88     if (low < 0) {
    89         carry = 1;
    90         low += (__m32+1);
    91     }
    92     var hi = (this.high32() - x.high32() - carry) | 0;
    93     return hi.next32(low);
    94 };
    95 
    96 Number.prototype.mul64 = function(x) {
    97     var low = this.mul32(x);
    98     low += (low < 0) ? (__m32+1) : 0;
    99     // first count upper 32 bits of (this.low * x.low)
   100     var hi_hi = 0;
   101     var hi_low = 0;
   102     var m = 1;
   103     for (var i = 0; i < 32; i++) {
   104         if (x & m) {
   105             hi_hi += this >>> 16;
   106             hi_low += this & 0xFFFF
   107         }
   108         hi_low >>= 1;
   109         hi_low += (hi_hi & 1) ? 0x8000 : 0;
   110         hi_hi >>= 1;
   111         m <<= 1;
   112     }
   113     var hi = (hi_hi << 16) + hi_low;
   114     
   115     var m1 = this.high32().mul32(x);
   116     var m2 = this.mul32(x.high32());
   117     hi = hi.add32(m1).add32(m2);
   118     
   119     return hi.next32(low);
   120 };
   121 
   122 Number.prototype.and64 = function(x) {
   123     var low = this & x;
   124     low += (low < 0) ? (__m32+1) : 0;
   125     if (this.hi && x.hi) {
   126         var hi = this.hi & x.hi;
   127         return hi.next32(low);
   128     };
   129     return low;
   130 };
   131 
   132 Number.prototype.or64 = function(x) {
   133     var low = this | x;
   134     low += (low < 0) ? (__m32+1) : 0;
   135     if (this.hi || x.hi) {
   136         var hi = this.hi | x.hi;
   137         return hi.next32(low);
   138     };
   139     return low;
   140 };
   141 
   142 Number.prototype.xor64 = function(x) {
   143     var low = this ^ x;
   144     low += (low < 0) ? (__m32+1) : 0;
   145     if (this.hi || x.hi) {
   146         var hi = this.hi ^ x.hi;
   147         return hi.next32(low);
   148     };
   149     return low;
   150 };
   151 
   152 Number.prototype.shl64 = function(x) {
   153     if (x >= 32) {
   154         var hi = this << (x - 32);
   155         return hi.next32(0);
   156     } else {
   157         var hi = this.high32() << x;
   158         var low_reminder = this >> (32 - x);
   159         hi |= low_reminder;
   160         var low = this << x;
   161         low += (low < 0) ? (__m32+1) : 0;
   162         return hi.next32(low);
   163     }
   164 };
   165 
   166 Number.prototype.shr64 = function(x) {
   167     if (x >= 32) {
   168         var low = this.high32() >> (x - 32);
   169         low += (low < 0) ? (__m32+1) : 0;
   170         return low;
   171     } else {
   172         var low = this >> x;
   173         var hi_reminder = this.high32() << (32 - x);
   174         low |= hi_reminder;
   175         low += (low < 0) ? (__m32+1) : 0;
   176         var hi = this.high32() >> x;
   177         return hi.next32(low);
   178     }
   179 };
   180 
   181 Number.prototype.ushr64 = function(x) {
   182     if (x >= 32) {
   183         var low = this.high32() >>> (x - 32);
   184         low += (low < 0) ? (__m32+1) : 0;
   185         return low;
   186     } else {
   187         var low = this >>> x;
   188         var hi_reminder = this.high32() << (32 - x);
   189         low |= hi_reminder;
   190         low += (low < 0) ? (__m32+1) : 0;
   191         var hi = this.high32() >>> x;
   192         return hi.next32(low);
   193     }
   194 };
   195 
   196 Number.prototype.compare64 = function(x) {
   197     if (this.high32() === x.high32()) {
   198         return (this < x) ? -1 : ((this > x) ? 1 : 0);
   199     }
   200     return (this.high32() < x.high32()) ? -1 : 1;
   201 };
   202 
   203 Number.prototype.neg64 = function() {
   204     var hi = this.high32();
   205     var low = this;
   206     if ((hi === 0) && (low < 0)) { return -low; }
   207     hi = ~hi;
   208     low = ~low;
   209     low += (low < 0) ? (__m32+1) : 0;
   210     var ret = hi.next32(low);
   211     return ret.add64(1);
   212 };
   213 
   214 (function(numberPrototype) {
   215     function __Int64(hi32, lo32) {
   216         this.hi32 = hi32 | 0;
   217         this.lo32 = lo32 | 0;
   218 
   219         this.get32 = function(bitIndex) {
   220             var v0;
   221             var v1;
   222             bitIndex += 32;
   223             var selector = bitIndex >>> 5;
   224             switch (selector) {
   225                 case 0:
   226                     v0 = 0;
   227                     v1 = this.lo32;
   228                     break;
   229                 case 1:
   230                     v0 = this.lo32;
   231                     v1 = this.hi32;
   232                     break;
   233                 case 2:
   234                     v0 = this.hi32;
   235                     v1 = 0;
   236                     break
   237                 default:
   238                     return 0;
   239             }
   240 
   241             var shift = bitIndex & 31;
   242             if (shift === 0) {
   243                 return v0;
   244             }
   245 
   246             return (v1 << (32 - shift)) | (v0 >>> shift);
   247         }
   248 
   249         this.get16 = function(bitIndex) {
   250             return this.get32(bitIndex) & 0xffff;
   251         }
   252 
   253         this.set16 = function(bitIndex, value) {
   254             bitIndex += 32;
   255             var shift = bitIndex & 15;
   256             var svalue = (value & 0xffff) << shift; 
   257             var smask = 0xffff << shift;
   258             var selector = bitIndex >>> 4;
   259             switch (selector) {
   260                 case 0:
   261                     break;
   262                 case 1:
   263                     this.lo32 = (this.lo32 & ~(smask >>> 16))
   264                                     | (svalue >>> 16);
   265                     break;
   266                 case 2:
   267                     this.lo32 = (this.lo32 & ~smask) | svalue;
   268                     break;
   269                 case 3:
   270                     this.lo32 = (this.lo32 & ~(smask << 16))
   271                                     | (svalue << 16);
   272                     this.hi32 = (this.hi32 & ~(smask >>> 16))
   273                                     | (svalue >>> 16);
   274                     break;
   275                 case 4:
   276                     this.hi32 = (this.hi32 & ~smask) | svalue;
   277                     break;
   278                 case 5:
   279                     this.hi32 = (this.hi32 & ~(smask << 16))
   280                                     | (svalue << 16);
   281                     break;
   282             }
   283         }
   284 
   285         this.getDigit = function(index, shift) {
   286             return this.get16((index << 4) - shift);
   287         }
   288 
   289         this.getTwoDigits = function(index, shift) {
   290             return this.get32(((index - 1) << 4) - shift);
   291         }
   292 
   293         this.setDigit = function(index, shift, value) {
   294             this.set16((index << 4) - shift, value);
   295         }
   296 
   297         this.countSignificantDigits = function() {
   298             var sd;
   299             var remaining;
   300 
   301             if (this.hi32 === 0) {
   302                 if (this.lo32 === 0) {
   303                     return 0;
   304                 }
   305 
   306                 sd = 2;
   307                 remaining = this.lo32;
   308             } else {
   309                 sd = 4;
   310                 remaining = this.hi32;
   311             }
   312 
   313             if (remaining < 0) {
   314                 return sd;
   315             }
   316 
   317             return (remaining < 65536) ? sd - 1 : sd;
   318         }
   319         
   320         this.toNumber = function() {
   321             var lo32 = this.lo32;
   322             if (lo32 < 0) {
   323                 lo32 += 0x100000000;
   324             }
   325 
   326             return this.hi32.next32(lo32);
   327         }
   328     }
   329 
   330     function __countLeadingZeroes16(number) {
   331         var nlz = 0;
   332 
   333         if (number < 256) {
   334             nlz += 8;
   335             number <<= 8;
   336         }
   337 
   338         if (number < 4096) {
   339             nlz += 4;
   340             number <<= 4;
   341         }
   342 
   343         if (number < 16384) {
   344             nlz += 2;
   345             number <<= 2;
   346         }
   347 
   348         return (number < 32768) ? nlz + 1 : nlz;
   349     }
   350     
   351     // q = u / v; r = u - q * v;
   352     // v != 0
   353     function __div64(q, r, u, v) {
   354         var m = u.countSignificantDigits();
   355         var n = v.countSignificantDigits();
   356 
   357         q.hi32 = q.lo32 = 0;
   358 
   359         if (n === 1) {
   360             // v has single digit
   361             var vd = v.getDigit(0, 0);
   362             var carry = 0;
   363             for (var i = m - 1; i >= 0; --i) {
   364                 var ui = (carry << 16) | u.getDigit(i, 0);
   365                 if (ui < 0) {
   366                     ui += 0x100000000;
   367                 }
   368                 var qi = (ui / vd) | 0;
   369                 q.setDigit(i, 0, qi);
   370                 carry = ui - qi * vd;
   371             }
   372 
   373             r.hi32 = 0;
   374             r.lo32 = carry;
   375             return;
   376         }
   377 
   378         r.hi32 = u.hi32;  
   379         r.lo32 = u.lo32;
   380 
   381         if (m < n) {
   382             return;
   383         }
   384 
   385         // Normalize
   386         var nrm = __countLeadingZeroes16(v.getDigit(n - 1, 0));
   387 
   388         var vd1 = v.getDigit(n - 1, nrm);                
   389         var vd0 = v.getDigit(n - 2, nrm);
   390         for (var j = m - n; j >= 0; --j) {
   391             // Calculate qj estimate
   392             var ud21 = r.getTwoDigits(j + n, nrm);
   393             var ud2 = ud21 >>> 16;
   394             if (ud21 < 0) {
   395                 ud21 += 0x100000000;
   396             }
   397 
   398             var qest = (ud2 === vd1) ? 0xFFFF : ((ud21 / vd1) | 0);
   399             var rest = ud21 - qest * vd1;
   400 
   401             // 0 <= (qest - qj) <= 2
   402 
   403             // Refine qj estimate
   404             var ud0 = r.getDigit(j + n - 2, nrm);
   405             while ((qest * vd0) > ((rest * 0x10000) + ud0)) {
   406                 --qest;
   407                 rest += vd1;
   408             }
   409 
   410             // 0 <= (qest - qj) <= 1
   411             
   412             // Multiply and subtract
   413             var carry = 0;
   414             for (var i = 0; i < n; ++i) {
   415                 var vi = qest * v.getDigit(i, nrm);
   416                 var ui = r.getDigit(i + j, nrm) - carry - (vi & 0xffff);
   417                 r.setDigit(i + j, nrm, ui);
   418                 carry = (vi >>> 16) - (ui >> 16);
   419             }
   420             var uj = ud2 - carry;
   421 
   422             if (uj < 0) {
   423                 // qest - qj = 1
   424 
   425                 // Add back
   426                 --qest;
   427                 var carry = 0;
   428                 for (var i = 0; i < n; ++i) {
   429                     var ui = r.getDigit(i + j, nrm) + v.getDigit(i, nrm)
   430                                  + carry;
   431                     r.setDigit(i + j, nrm, ui);
   432                     carry = ui >> 16;
   433                 }
   434                 uj += carry;
   435             }
   436 
   437             q.setDigit(j, 0, qest);
   438             r.setDigit(j + n, nrm, uj);
   439         }
   440     }
   441     
   442     numberPrototype.div64 = function(x) {
   443         var negateResult = false;
   444         var u, v;
   445         
   446         if ((this.high32() & 0x80000000) != 0) {
   447             u = this.neg64();
   448             negateResult = !negateResult;
   449         } else {
   450             u = this;        
   451         }
   452 
   453         if ((x.high32() & 0x80000000) != 0) {
   454             v = x.neg64();
   455             negateResult = !negateResult;
   456         } else {
   457             v = x;
   458         }
   459 
   460         if ((v === 0) && (v.high32() === 0)) {
   461             // TODO: throw
   462         }
   463 
   464         if (u.high32() === 0) {
   465             if (v.high32() === 0) {
   466                 var result = (u / v) | 0;
   467                 return negateResult ? result.neg64() : result; 
   468             }
   469 
   470             return 0;
   471         }
   472 
   473         var u64 = new __Int64(u.high32(), u);
   474         var v64 = new __Int64(v.high32(), v);
   475         var q64 = new __Int64(0, 0);
   476         var r64 = new __Int64(0, 0);
   477 
   478         __div64(q64, r64, u64, v64);
   479 
   480         var result = q64.toNumber();
   481         return negateResult ? result.neg64() : result; 
   482     }
   483 
   484     numberPrototype.mod64 = function(x) {
   485         var negateResult = false;
   486         var u, v;
   487         
   488         if ((this.high32() & 0x80000000) != 0) {
   489             u = this.neg64();
   490             negateResult = !negateResult;
   491         } else {
   492             u = this;        
   493         }
   494 
   495         if ((x.high32() & 0x80000000) != 0) {
   496             v = x.neg64();
   497         } else {
   498             v = x;
   499         }
   500 
   501         if ((v === 0) && (v.high32() === 0)) {
   502             // TODO: throw
   503         }
   504 
   505         if (u.high32() === 0) {
   506             var result = (v.high32() === 0) ? (u % v) : u;
   507             return negateResult ? result.neg64() : result; 
   508         }
   509 
   510         var u64 = new __Int64(u.high32(), u);
   511         var v64 = new __Int64(v.high32(), v);
   512         var q64 = new __Int64(0, 0);
   513         var r64 = new __Int64(0, 0);
   514 
   515         __div64(q64, r64, u64, v64);
   516 
   517         var result = r64.toNumber();
   518         return negateResult ? result.neg64() : result; 
   519     }
   520 })(Number.prototype);