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